From d4c1aad008faa32011e03ceec2a72f026c616a86 Mon Sep 17 00:00:00 2001 From: dingjsh <47954233@qq.com> Date: Thu, 11 Oct 2018 19:40:22 +0800 Subject: [PATCH 01/25] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E5=88=86=E6=9E=90=E5=B7=A5=E5=85=B7=20h2=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E7=9A=84=E4=BD=BF=E7=94=A8=E8=AF=B4=E6=98=8E=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d50ab14..f0de7a3 100644 --- a/README.md +++ b/README.md @@ -24,5 +24,10 @@ agent.exclude.class.regex.default=.*EnhancerByCGLIB.*;.*FastClassByCGLIB.* # 记录方法的耗时时是采用nanoTime,还是currentTimeMillis,nanoTime更准确,但是会耗时一些 agent.log.nano=true ``` - - + +## agent日志分析器使用 +agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分析工具,除了基础功能,对于有sql能力的同学,你可以直接使用[console](http://47.96.150.36:8080/console)登录后,直接写sql进行统计,你可以按自己想要的维度来进行处理。 +- JDBC URL: jdbc:h2:mem:~/h2test +- USer Name : sa +- Password : 空 +登录后,你就可以尽情的写sql了。 \ No newline at end of file From 965196d7696e4fda55215fa613cba446cc4014e7 Mon Sep 17 00:00:00 2001 From: dingjs Date: Wed, 6 May 2020 20:53:24 +0800 Subject: [PATCH 02/25] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f0de7a3..444b246 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,7 @@ agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分 - JDBC URL: jdbc:h2:mem:~/h2test - USer Name : sa - Password : 空 -登录后,你就可以尽情的写sql了。 \ No newline at end of file +登录后,你就可以尽情的写sql了。 + +## 欢迎关注我的公众号 +![程序员阿水](https://ftp.bmp.ovh/imgs/2020/05/450fcb3d1a2fe4eb.jpg) From 8c9884d3a2faac1d3947c06b2d58da56d9b5c8c8 Mon Sep 17 00:00:00 2001 From: dingjs Date: Wed, 6 May 2020 20:53:55 +0800 Subject: [PATCH 03/25] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 444b246..7a47736 100644 --- a/README.md +++ b/README.md @@ -33,4 +33,5 @@ agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分 登录后,你就可以尽情的写sql了。 ## 欢迎关注我的公众号 +- 程序员阿水 ![程序员阿水](https://ftp.bmp.ovh/imgs/2020/05/450fcb3d1a2fe4eb.jpg) From e2fda3f1f11d39712f00be875c4f96fae4fa57a2 Mon Sep 17 00:00:00 2001 From: dingjs Date: Wed, 6 May 2020 20:54:50 +0800 Subject: [PATCH 04/25] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7a47736..d08247e 100644 --- a/README.md +++ b/README.md @@ -34,4 +34,6 @@ agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分 ## 欢迎关注我的公众号 - 程序员阿水 + + ![程序员阿水](https://ftp.bmp.ovh/imgs/2020/05/450fcb3d1a2fe4eb.jpg) From 2e2ad7119641bf4f2503de1daf6869764eb72c4c Mon Sep 17 00:00:00 2001 From: bean <523084685@qq.com> Date: Wed, 13 May 2020 13:37:32 +0800 Subject: [PATCH 05/25] =?UTF-8?q?fix=20:bug:=20=E4=BF=AE=E5=A4=8D=E6=8B=BC?= =?UTF-8?q?=E5=86=99=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d08247e..c50e7c7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ javaagent是一个简单优雅的java agent,利用java自带的instrument特性+javassist字节码编辑技术,实现了无侵入的方法级性能监控。相比于NewRelic或者开源的[pinpoint](https://github.com/naver/pinpoint),以及阿里的[arthas](https://github.com/alibaba/arthas),本工具主打的是简单,我们只记录每个方法的执行次数和时间,并输出到json格式的日志文件中。基于javaagent的日志,你可以使用严丽同学开发的[agent日志分析工具](https://pan.baidu.com/s/1Ma4iEWRmBonGO1TapeEF-g)进行分析查询,也可以使用[在线日志分析器](http://47.96.150.36:8080),或者可以自己去写分析器,这样可以让你快速定位生产环境的性能瓶颈。 ## 集成 -java启动参数中就有jaaagent,你只需要在JAVA_OPTS中加入`-javaagent:/opt/javaagent/javaagent.jar=/opt/javaagent/agent.properties`就实现了方法级监控。其中`=`前指定的是jar包的路径,`=`后指定的是对agent的一些配置参数。 +java启动参数中就有javaagent,你只需要在JAVA_OPTS中加入`-javaagent:/opt/javaagent/javaagent.jar=/opt/javaagent/agent.properties`就实现了方法级监控。其中`=`前指定的是jar包的路径,`=`后指定的是对agent的一些配置参数。 ### agent.properties说明 ``` From 85162ee7f1438ae0a8508bdb885d2fa46d861cc3 Mon Sep 17 00:00:00 2001 From: dingjs Date: Fri, 2 Apr 2021 19:36:28 +0800 Subject: [PATCH 06/25] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c50e7c7..ca64c49 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Javaagent ## 概述 -javaagent是一个简单优雅的java agent,利用java自带的instrument特性+javassist字节码编辑技术,实现了无侵入的方法级性能监控。相比于NewRelic或者开源的[pinpoint](https://github.com/naver/pinpoint),以及阿里的[arthas](https://github.com/alibaba/arthas),本工具主打的是简单,我们只记录每个方法的执行次数和时间,并输出到json格式的日志文件中。基于javaagent的日志,你可以使用严丽同学开发的[agent日志分析工具](https://pan.baidu.com/s/1Ma4iEWRmBonGO1TapeEF-g)进行分析查询,也可以使用[在线日志分析器](http://47.96.150.36:8080),或者可以自己去写分析器,这样可以让你快速定位生产环境的性能瓶颈。 +javaagent是一个简单优雅的java agent,利用java自带的instrument特性+javassist字节码编辑技术,实现了无侵入的方法级性能监控。相比于NewRelic或者开源的[pinpoint](https://github.com/naver/pinpoint),以及阿里的[arthas](https://github.com/alibaba/arthas),本工具主打的是简单,我们只记录每个方法的执行次数和时间,并输出到json格式的日志文件中。基于javaagent的日志,你可以使用严丽同学开发的[agent日志分析工具](https://pan.baidu.com/s/1Ma4iEWRmBonGO1TapeEF-g)进行分析查询,或者可以自己去写分析器,这样可以让你快速定位生产环境的性能瓶颈。 ## 集成 java启动参数中就有javaagent,你只需要在JAVA_OPTS中加入`-javaagent:/opt/javaagent/javaagent.jar=/opt/javaagent/agent.properties`就实现了方法级监控。其中`=`前指定的是jar包的路径,`=`后指定的是对agent的一些配置参数。 From ed0ed7dc6c3965d01e00a78f1dfb1ba82dd54002 Mon Sep 17 00:00:00 2001 From: dingjs Date: Thu, 29 Apr 2021 09:07:04 +0800 Subject: [PATCH 07/25] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ca64c49..397c6cb 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,5 @@ agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分 ## 欢迎关注我的公众号 - 程序员阿水 +![程序员阿水](https://user-images.githubusercontent.com/3361218/116490540-3ae6da80-a8ca-11eb-97b9-5b9f41dc7740.jpg) - -![程序员阿水](https://ftp.bmp.ovh/imgs/2020/05/450fcb3d1a2fe4eb.jpg) From ebd97903fe71d10cf6417c554b1f751fa97578a3 Mon Sep 17 00:00:00 2001 From: dingjs Date: Thu, 29 Apr 2021 09:07:30 +0800 Subject: [PATCH 08/25] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 397c6cb..9cc5e4f 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分 登录后,你就可以尽情的写sql了。 ## 欢迎关注我的公众号 -- 程序员阿水 +- 程序员阿水 + ![程序员阿水](https://user-images.githubusercontent.com/3361218/116490540-3ae6da80-a8ca-11eb-97b9-5b9f41dc7740.jpg) From bbfa87c1778c528964a5ac83b25cccd44a948899 Mon Sep 17 00:00:00 2001 From: dingjs Date: Thu, 29 Apr 2021 09:08:33 +0800 Subject: [PATCH 09/25] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cc5e4f..8736aa2 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ agent.log.nano=true ``` ## agent日志分析器使用 -agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分析工具,除了基础功能,对于有sql能力的同学,你可以直接使用[console](http://47.96.150.36:8080/console)登录后,直接写sql进行统计,你可以按自己想要的维度来进行处理。 +agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分析工具,除了基础功能,对于有sql能力的同学,你可以直接使用[agent日志分析工具](https://pan.baidu.com/s/1Ma4iEWRmBonGO1TapeEF-g)自带的[console](http://XXXX/console)登录后,直接写sql进行统计,你可以按自己想要的维度来进行处理。 - JDBC URL: jdbc:h2:mem:~/h2test - USer Name : sa - Password : 空 From 34a7f59b6b3f9cac1d53822da0880ccc55ee06f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=BB=BA=E6=B0=B4?= Date: Wed, 25 Aug 2021 10:46:56 +0800 Subject: [PATCH 10/25] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=9D=8F=E5=91=B3=E9=81=93=EF=BC=8C=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- src/com/wenshuo/agent/Agent.java | 8 +-- src/com/wenshuo/agent/AgentUtils.java | 62 +++++++++---------- src/com/wenshuo/agent/ConfigUtils.java | 29 ++++----- src/com/wenshuo/agent/ExecuteLogAnalyzer.java | 39 ------------ src/com/wenshuo/agent/NamedThreadFactory.java | 14 ----- src/com/wenshuo/agent/PojoDetector.java | 6 +- .../wenshuo/agent/log/ExecuteLogUtils.java | 56 ++++++++--------- .../agent/log/MethodExecuteJSONformatter.java | 29 ++++----- .../AgentLogClassFileTransformer.java | 10 +-- 10 files changed, 94 insertions(+), 161 deletions(-) delete mode 100644 src/com/wenshuo/agent/ExecuteLogAnalyzer.java diff --git a/pom.xml b/pom.xml index 6812957..4e771ad 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.wenshuo javaagent - 2.1.0 + 2.1.1 jar diff --git a/src/com/wenshuo/agent/Agent.java b/src/com/wenshuo/agent/Agent.java index f655005..1bfd12b 100644 --- a/src/com/wenshuo/agent/Agent.java +++ b/src/com/wenshuo/agent/Agent.java @@ -5,22 +5,18 @@ * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package com.wenshuo.agent; - -import java.io.IOException; import java.lang.instrument.Instrumentation; - import com.wenshuo.agent.log.ExecuteLogUtils; import com.wenshuo.agent.transformer.AgentLogClassFileTransformer; /** * Agent - * + * * @author dingjsh */ public class Agent { - public static void premain(String agentArs, Instrumentation inst) - throws IOException { + public static void premain(String agentArs, Instrumentation inst) { // 初始化配置 ConfigUtils.initProperties(agentArs); ExecuteLogUtils.init(); diff --git a/src/com/wenshuo/agent/AgentUtils.java b/src/com/wenshuo/agent/AgentUtils.java index 6ee752b..e133898 100644 --- a/src/com/wenshuo/agent/AgentUtils.java +++ b/src/com/wenshuo/agent/AgentUtils.java @@ -15,7 +15,7 @@ /** * AgentUtils - * + * * @author dingjsh * @time 2015-7-27下午05:26:24 */ @@ -25,7 +25,7 @@ public class AgentUtils { *

* Checks if a String is whitespace, empty ("") or null. *

- * + * *
      * StringUtils.isBlank(null)      = true
      * StringUtils.isBlank("")        = true
@@ -33,9 +33,8 @@ public class AgentUtils {
      * StringUtils.isBlank("bob")     = false
      * StringUtils.isBlank("  bob  ") = false
      * 
- * - * @param str - * the String to check, may be null + * + * @param str the String to check, may be null * @return true if the String is null, empty or whitespace * @since 2.0 */ @@ -45,7 +44,7 @@ public static boolean isBlank(String str) { return true; } for (int i = 0; i < strLen; i++) { - if ((Character.isWhitespace(str.charAt(i)) == false)) { + if (!Character.isWhitespace(str.charAt(i))) { return false; } } @@ -56,7 +55,7 @@ public static boolean isBlank(String str) { *

* Checks if a String is not empty (""), not null and not whitespace only. *

- * + * *
      * StringUtils.isNotBlank(null)      = false
      * StringUtils.isNotBlank("")        = false
@@ -64,14 +63,13 @@ public static boolean isBlank(String str) {
      * StringUtils.isNotBlank("bob")     = true
      * StringUtils.isNotBlank("  bob  ") = true
      * 
- * - * @param str - * the String to check, may be null + * + * @param str the String to check, may be null * @return true if the String is not empty and not null and not - * whitespace + * whitespace * @since 2.0 */ - public static boolean isNotBlank(String str) { + static boolean isNotBlank(String str) { return !isBlank(str); } @@ -80,11 +78,10 @@ public static boolean isNotBlank(String str) { *

* Equivalent to {@link InputStream#close()}, except any exceptions will be * ignored. This is typically used in finally blocks. - * - * @param input - * the InputStream to close, may be null or already closed + * + * @param input the InputStream to close, may be null or already closed */ - public static void closeQuietly(InputStream input) { + static void closeQuietly(InputStream input) { try { if (input != null) { input.close(); @@ -103,44 +100,45 @@ public static int copy(InputStream input, OutputStream output) return (int) count; } - public static long copyLarge(InputStream input, OutputStream output) + private static long copyLarge(InputStream input, OutputStream output) throws IOException { byte[] buffer = new byte[4096]; long count = 0; - int n = 0; + int n; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); count += n; } return count; } - + // read toByteArray //----------------------------------------------------------------------- + /** * Get the contents of an InputStream as a byte[]. *

* This method buffers the input internally, so there is no need to use a * BufferedInputStream. - * - * @param input the InputStream to read from + * + * @param input the InputStream to read from * @return the requested byte array * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs + * @throws IOException if an I/O error occurs */ public static byte[] toByteArray(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); copy(input, output); return output.toByteArray(); } - + /** * Unconditionally close a Writer. *

* Equivalent to {@link Writer#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * - * @param output the Writer to close, may be null or already closed + * @param output the Writer to close, may be null or already closed */ public static void closeQuietly(Writer output) { try { @@ -151,30 +149,30 @@ public static void closeQuietly(Writer output) { // ignore } } - + /** * Makes a directory, including any necessary but nonexistent parent * directories. If there already exists a file with specified name or * the directory cannot be created then an exception is thrown. * - * @param directory directory to create, must not be null + * @param directory directory to create, must not be null * @throws NullPointerException if the directory is null - * @throws IOException if the directory cannot be created + * @throws IOException if the directory cannot be created */ public static void forceMkdir(File directory) throws IOException { if (directory.exists()) { if (directory.isFile()) { String message = - "File " - + directory - + " exists and is " - + "not a directory. Unable to create directory."; + "File " + + directory + + " exists and is " + + "not a directory. Unable to create directory."; throw new IOException(message); } } else { if (!directory.mkdirs()) { String message = - "Unable to create directory " + directory; + "Unable to create directory " + directory; throw new IOException(message); } } diff --git a/src/com/wenshuo/agent/ConfigUtils.java b/src/com/wenshuo/agent/ConfigUtils.java index 5fd66ca..4de6036 100644 --- a/src/com/wenshuo/agent/ConfigUtils.java +++ b/src/com/wenshuo/agent/ConfigUtils.java @@ -11,7 +11,7 @@ /** * 获取配置信息
* ConfigUtils - * + * * @author dingjsh * @time 2015-7-27下午09:16:18 */ @@ -29,14 +29,14 @@ private ConfigUtils() { super(); } - public static void initProperties(String propertiesFileName) { + static void initProperties(String propertiesFileName) { props = getProperties(propertiesFileName); initExcluePackages(); initIncludePackages(); initExcludeClassRegexs(); } - public static String getProperty(String key) { + private static String getProperty(String key) { if (null != props && AgentUtils.isNotBlank(key)) { return props.getProperty(key); } @@ -76,10 +76,10 @@ private static void initExcluePackages() { String excludeDefault = getProperty(ConfigConsts.EXCLUDE_PACKAGE_DEFAULT); String excludes = getProperty(ConfigConsts.EXCLUDE_PACKAGE); excludePackages = new HashSet(); - if (null != excludeDefault && AgentUtils.isNotBlank(excludeDefault)) { + if (AgentUtils.isNotBlank(excludeDefault)) { excludePackages.addAll(Arrays.asList(excludeDefault.split(";"))); } - if (null != excludes && AgentUtils.isNotBlank(excludes)) { + if (AgentUtils.isNotBlank(excludes)) { excludePackages.addAll(Arrays.asList(excludes.split(";"))); } } @@ -93,7 +93,7 @@ private static void initIncludePackages() { if (null == includePackages) { String includes = getProperty(ConfigConsts.INCLUDE_PACKAGE); includePackages = new HashSet(); - if (null != includes && AgentUtils.isNotBlank(includes)) { + if (AgentUtils.isNotBlank(includes)) { includePackages.addAll(Arrays.asList(includes.split(";"))); } } @@ -119,11 +119,11 @@ private static void initExcludeClassRegexs() { if (null == excludeClassRegexs) { Set excludeClassRegexsTemp = new HashSet(); String defaultRegex = getProperty(ConfigConsts.EXCLUDE_CLASS_REGEX_DEFAULT); - if (null != defaultRegex && AgentUtils.isNotBlank(defaultRegex)) { + if (AgentUtils.isNotBlank(defaultRegex)) { excludeClassRegexsTemp.addAll(Arrays.asList(defaultRegex.split(";"))); } String excludeRegexStr = getProperty(ConfigConsts.EXCLUDE_CLASS_REGEX); - if (null != excludeRegexStr && AgentUtils.isNotBlank(excludeRegexStr)) { + if (AgentUtils.isNotBlank(excludeRegexStr)) { excludeClassRegexsTemp.addAll(Arrays.asList(excludeRegexStr.split(";"))); } excludeClassRegexs = excludeClassRegexsTemp; @@ -137,25 +137,26 @@ public static boolean isLogAvgExecuteTime() { /** * 是否开启pojo的监控 - * - * @return + * + * @return 是否开启pojo的监控 * @author dingjsh */ public static boolean isOpenPojoMonitor() { String value = getProperty(ConfigConsts.POJO_MONITOR_OPEN); return "true".equalsIgnoreCase(value); } - + /** * ConfigUtils + * + * @return 是否采用nanoTime来记录方法的执行时间 * @description 是否采用nanoTime来记录方法的执行时间 - * @return * @author dingjsh * @date 2018年5月25日 下午2:08:33 * @version 1.2.0 */ - public static boolean isUsingNanoTime(){ - String value = getProperty(ConfigConsts.LOG_TIME_NANO); + public static boolean isUsingNanoTime() { + String value = getProperty(ConfigConsts.LOG_TIME_NANO); return "true".equalsIgnoreCase(value); } } diff --git a/src/com/wenshuo/agent/ExecuteLogAnalyzer.java b/src/com/wenshuo/agent/ExecuteLogAnalyzer.java deleted file mode 100644 index 1d40630..0000000 --- a/src/com/wenshuo/agent/ExecuteLogAnalyzer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * @(#)ExecuteLogAnalyzer.java 2015-8-3 上午09:15:41 - * javaagent - * Copyright 2015 wenshuo, Inc. All rights reserved. - * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ -package com.wenshuo.agent; - -/** - * ExecuteLogAnalyzer,分析方法执行日志 - * - * @author dingjsh - * @time 2015-8-3上午09:15:41 - */ -public class ExecuteLogAnalyzer { - - /** - * 日志分析 - * @param logFileName 日志文件名 - * @param topExecuteCount 执行次数最多的top数 - * @param executeTimeThreshold 执行时间的阀值 - * @author dingjsh - * @time 2015-8-3上午09:18:18 - */ - public void analyze(String logFileName,int topExecuteCount,int executeTimeThreshold){ - - - - } - - - - public static void main(String[] args) { - - } - - - -} diff --git a/src/com/wenshuo/agent/NamedThreadFactory.java b/src/com/wenshuo/agent/NamedThreadFactory.java index 61c007d..6daceec 100644 --- a/src/com/wenshuo/agent/NamedThreadFactory.java +++ b/src/com/wenshuo/agent/NamedThreadFactory.java @@ -10,8 +10,6 @@ * @time 2016-5-19上午09:40:50 */ public class NamedThreadFactory implements ThreadFactory { - private static final AtomicInteger POOL_SEQ = new AtomicInteger(1); - private final AtomicInteger mThreadNum = new AtomicInteger(1); private final String mPrefix; @@ -20,14 +18,6 @@ public class NamedThreadFactory implements ThreadFactory { private final ThreadGroup mGroup; - public NamedThreadFactory() { - this("pool-" + POOL_SEQ.getAndIncrement(), false); - } - - public NamedThreadFactory(String prefix) { - this(prefix, false); - } - public NamedThreadFactory(String prefix, boolean daemon) { mPrefix = prefix + "-thread-"; mDaemon = daemon; @@ -42,8 +32,4 @@ public Thread newThread(Runnable runnable) { ret.setDaemon(mDaemon); return ret; } - - public ThreadGroup getThreadGroup() { - return mGroup; - } } \ No newline at end of file diff --git a/src/com/wenshuo/agent/PojoDetector.java b/src/com/wenshuo/agent/PojoDetector.java index ad471ca..48cf45c 100644 --- a/src/com/wenshuo/agent/PojoDetector.java +++ b/src/com/wenshuo/agent/PojoDetector.java @@ -20,11 +20,7 @@ * @time 2016-5-7下午04:18:14 */ public class PojoDetector { - -// private static final String[] ignoreMthods = new String[]{"toString","equals","hashCode","toJSONString"}; - - - + public static Set getPojoMethodNames(CtMethod[] methods){ Set pojoMethods = Collections.emptySet(); if(null != methods && methods.length>0){ diff --git a/src/com/wenshuo/agent/log/ExecuteLogUtils.java b/src/com/wenshuo/agent/log/ExecuteLogUtils.java index 9b00887..0786543 100644 --- a/src/com/wenshuo/agent/log/ExecuteLogUtils.java +++ b/src/com/wenshuo/agent/log/ExecuteLogUtils.java @@ -28,7 +28,7 @@ /** * ExecuteLogUtils - * + * * @author dingjsh * @time 2015-7-27下午05:57:01 */ @@ -40,11 +40,11 @@ public class ExecuteLogUtils { private static String logFileName = null; - private static long startTimemillis; + private static long startTimeMillis; private static ScheduledThreadPoolExecutor counterLogExecutor; - private static Map> exeuteCounterMap; + private static Map> executeCounterMap; private static final Object executeCounterLock = new Object(); @@ -82,53 +82,53 @@ public static synchronized void init() { } setNextDateStartTimeMillis(); initWriter(); - exeuteCounterMap = new ConcurrentHashMap>(); - startTimemillis = System.currentTimeMillis(); + executeCounterMap = new ConcurrentHashMap>(); + startTimeMillis = System.currentTimeMillis(); counterLogExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("pool-thread-agent-log", true)); counterLogExecutor.scheduleWithFixedDelay(new OutputLogRunnable(), interval, interval, TimeUnit.SECONDS); inited = true; } - public static void log(String className, String methodName, long currentTimemillis, long executeTime) { + public static void log(String className, String methodName, long executeTime) { logExecuteCounter(className, methodName, executeTime); } /** * 输出方法执行记数日志 - * + * * @author dingjsh * @time 2015-7-28下午01:35:25 */ - public synchronized static void outputCounterLog() throws IOException { - Map> exeuteCounterMapInner = exeuteCounterMap; - exeuteCounterMap = new ConcurrentHashMap>(); - String startTime = foramteTimeMillis(startTimemillis); - String endTime = foramteTimeMillis(System.currentTimeMillis()); + synchronized static void outputCounterLog() throws IOException { + Map> executeCounterMapInner = executeCounterMap; + executeCounterMap = new ConcurrentHashMap>(); + String startTime = formatTimeMillis(startTimeMillis); + String endTime = formatTimeMillis(System.currentTimeMillis()); long byteLength = removeJSONArrayEndBracket(); if (0 == byteLength) { - writeLog("[",true, startTimemillis); + writeLog("[",true, startTimeMillis); } - Set>> entrySet = exeuteCounterMapInner.entrySet(); + Set>> entrySet = executeCounterMapInner.entrySet(); Iterator>> ite = entrySet.iterator(); int length = entrySet.size(); if (length > 0 && byteLength > 10) { // 说明文件不只是[],json数组中已经有内容 - writeLog(",", startTimemillis); + writeLog(",", startTimeMillis); } for (int index = 0; ite.hasNext(); index++) { Map.Entry> entry = ite.next(); String className = entry.getKey(); Map method2ExecuteMap = entry.getValue(); String methodExecuteJson = MethodExecuteJSONformatter.getMethodExecuteJSON(className, method2ExecuteMap, - startTime, endTime, isUsingNanoTime, logAvgExecuteTime); - writeLog(methodExecuteJson, startTimemillis); + startTime, endTime, isUsingNanoTime, logAvgExecuteTime); + writeLog(methodExecuteJson, startTimeMillis); if (index < length - 1) { - writeLog(",", true, startTimemillis); + writeLog(",", true, startTimeMillis); } } - writeLog("]", true,startTimemillis); + writeLog("]", true, startTimeMillis); flushLogAndClose(); - startTimemillis = System.currentTimeMillis(); + startTimeMillis = System.currentTimeMillis(); } private static void logExecuteCounter(String className, String methodName, long executeTime) { @@ -151,9 +151,9 @@ private static void logExecuteCounter(String className, String methodName, long } /** - * + * * ExecuteLogUtils - * + * * @description 获得class和它的调用次数映射关系,如果exeuteCounterMap中没有,则创建一个并放入 * @param className 类名 * @return class和它的调用次数映射关系 @@ -162,13 +162,13 @@ private static void logExecuteCounter(String className, String methodName, long * @version 1.2.0 */ private static Map getOrCreateClassExecutesMapping(String className) { - Map methodCounterMap = exeuteCounterMap.get(className); + Map methodCounterMap = executeCounterMap.get(className); if (null == methodCounterMap) { synchronized (executeCounterLock) { - methodCounterMap = exeuteCounterMap.get(className); + methodCounterMap = executeCounterMap.get(className); if (null == methodCounterMap) { methodCounterMap = new ConcurrentHashMap(); - exeuteCounterMap.put(className, methodCounterMap); + executeCounterMap.put(className, methodCounterMap); } } } @@ -230,7 +230,7 @@ private static void initWriter() { try { File logFile = getCounterLogFile(logFileName); counterLogWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile, true), ENCODING), - BUFFER_SIZE); + BUFFER_SIZE); } catch (IOException e) { System.err.println(e); throw new RuntimeException("无法初始化【" + logFileName + "】,建议您检查磁盘空间,或者手动删除该日志文件"); @@ -243,7 +243,7 @@ private static File getCounterLogFile(String logFileName) throws IOException { int lastIndexOfDot = logFileName.lastIndexOf('.'); if (lastIndexOfDot > 0) { logFileName = logFileName.substring(0, lastIndexOfDot) + "." + logFileNameWithDate - + logFileName.substring(lastIndexOfDot); + + logFileName.substring(lastIndexOfDot); } else { logFileName = logFileName + "." + logFileNameWithDate + ".log"; } @@ -301,7 +301,7 @@ private static void setNextDateStartTimeMillis() { nextDayStartTimeMillis = startTime.getTime(); } - private static String foramteTimeMillis(long timeMillis) { + private static String formatTimeMillis(long timeMillis) { Date date = new Date(timeMillis); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); diff --git a/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java b/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java index a35225a..a886f08 100644 --- a/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java +++ b/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java @@ -1,49 +1,44 @@ package com.wenshuo.agent.log; -import java.util.Iterator; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; /** - * * MethodExecuteJSONformatter - * - * @description 2.0.0 + * * @author dingjsh - * @date 2018年8月22日 下午8:07:01 * @version 2.0.0 + * @description 2.0.0 + * @date 2018年8月22日 下午8:07:01 */ class MethodExecuteJSONformatter { /** - * * MethodExecuteJSONformatter - * - * @description 将方法执行监控数据格式化为json string - * @param className 类名 + * + * @param className 类名 * @param method2ExecuteMap Map<方法名, AtomicLong[]> 数组第一个是执行次数,第二个是执行时间 - * @param startTime 统计周期的开始时间,格式为 yyyy-MM-dd HH:mm:ss - * @param endTime 统计周期的结束时间,格式为 yyyy-MM-dd HH:mm:ss + * @param startTime 统计周期的开始时间,格式为 yyyy-MM-dd HH:mm:ss + * @param endTime 统计周期的结束时间,格式为 yyyy-MM-dd HH:mm:ss * @return 格式化后的json string + * @description 将方法执行监控数据格式化为json string * @author dingjsh * @date 2018年8月22日 下午8:49:18 * @version 2.0.0 */ - public static String getMethodExecuteJSON(String className, Map method2ExecuteMap, - String startTime, String endTime, boolean isUsingNanoTime, boolean logAvgExecuteTime) { + static String getMethodExecuteJSON(String className, Map method2ExecuteMap, + String startTime, String endTime, boolean isUsingNanoTime, boolean logAvgExecuteTime) { StringBuilder json = new StringBuilder("{"); appendString(json, "class", className).append(","); appendString(json, "start", startTime).append(","); appendString(json, "end", endTime).append(","); appendKey(json, "methods").append("["); - Iterator> method2ExecuteIte = method2ExecuteMap.entrySet().iterator(); - while (method2ExecuteIte.hasNext()) { - Map.Entry methodEntry = method2ExecuteIte.next(); + for (Map.Entry methodEntry : method2ExecuteMap.entrySet()) { String methodName = methodEntry.getKey(); AtomicLong[] executeCounter = methodEntry.getValue(); long counter = executeCounter[0].longValue(); long timeInMillis - = isUsingNanoTime ? executeCounter[1].longValue() / 1000000 : executeCounter[1].longValue(); + = isUsingNanoTime ? executeCounter[1].longValue() / 1000000 : executeCounter[1].longValue(); json.append("{"); appendString(json, "name", methodName).append(","); appendLong(json, "counter", counter).append(","); diff --git a/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java b/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java index 3eaf15d..23f54a9 100644 --- a/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java +++ b/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java @@ -40,7 +40,7 @@ public class AgentLogClassFileTransformer implements ClassFileTransformer { * byte[]) */ public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, - ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { + ProtectionDomain protectionDomain, byte[] classfileBuffer) { byte[] byteCode = classfileBuffer; className = className.replace('/', '.'); if (!isNeedLogExecuteInfo(className)) { @@ -56,7 +56,7 @@ public byte[] transform(ClassLoader loader, String className, Class classBein private byte[] aopLog(ClassLoader loader, String className, byte[] byteCode) { try { ClassPool cp = ClassPool.getDefault(); - CtClass cc = null; + CtClass cc; try { cc = cp.get(className); } catch (NotFoundException e) { @@ -108,14 +108,14 @@ private void aopLog(String className, CtMethod m) throws CannotCompileException m.insertBefore("dingjsh_javaagent_elapsedTime = " + timeMethodStr + ";"); m.insertAfter("dingjsh_javaagent_elapsedTime = " + timeMethodStr + " - dingjsh_javaagent_elapsedTime;"); m.insertAfter(LOG_UTILS + ".log(" + aopClassName + ",\"" + m.getName() - + "\",java.lang.System.currentTimeMillis(),(long)dingjsh_javaagent_elapsedTime" + ");"); + + "\",(long)dingjsh_javaagent_elapsedTime" + ");"); } /** * 是否需要记录执行信息 * - * @param className - * @return + * @param className 类名 + * @return 是否需要记录执行信息 * @author dingjsh * @time 2015-7-27下午06:11:02 */ From fc2e2bbc66726e53d0467f6470056b865e041b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=BB=BA=E6=B0=B4?= Date: Wed, 25 Aug 2021 14:29:55 +0800 Subject: [PATCH 11/25] fix typo --- src/com/wenshuo/agent/log/ExecuteLogUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/wenshuo/agent/log/ExecuteLogUtils.java b/src/com/wenshuo/agent/log/ExecuteLogUtils.java index 0786543..ac2851f 100644 --- a/src/com/wenshuo/agent/log/ExecuteLogUtils.java +++ b/src/com/wenshuo/agent/log/ExecuteLogUtils.java @@ -56,7 +56,7 @@ public class ExecuteLogUtils { private static boolean logAvgExecuteTime = false; - private static boolean inited = false; + private static volatile boolean isInitialized = false; private ExecuteLogUtils() { super(); @@ -69,7 +69,7 @@ private ExecuteLogUtils() { * @time 2015-7-30上午10:47:58 */ public static synchronized void init() { - if (inited) { + if (isInitialized) { return; } logFileName = ConfigUtils.getLogFileName(); @@ -86,7 +86,7 @@ public static synchronized void init() { startTimeMillis = System.currentTimeMillis(); counterLogExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("pool-thread-agent-log", true)); counterLogExecutor.scheduleWithFixedDelay(new OutputLogRunnable(), interval, interval, TimeUnit.SECONDS); - inited = true; + isInitialized = true; } public static void log(String className, String methodName, long executeTime) { From 0746419348a7b1527c9822e163cb332ae2b10f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=BB=BA=E6=B0=B4?= Date: Wed, 25 Aug 2021 14:58:57 +0800 Subject: [PATCH 12/25] =?UTF-8?q?=E5=B0=86synchonized=E5=92=8CAtomicLong?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E6=94=B9=E4=B8=BA=E6=97=A0=E9=94=81=EF=BC=8C?= =?UTF-8?q?=E7=89=BA=E7=89=B2=E4=B8=80=E4=BA=9B=E7=BB=9F=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=87=86=E7=A1=AE=E6=80=A7=E5=B8=A6=E6=9D=A5=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E6=8F=90=E5=8D=87=E5=92=8C=E5=87=8F=E4=BD=8E=E5=86=85?= =?UTF-8?q?=E5=AD=98=E6=B6=88=E8=80=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wenshuo/agent/log/ExecuteLogUtils.java | 50 ++++++++----------- .../agent/log/MethodExecuteJSONformatter.java | 14 +++--- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/com/wenshuo/agent/log/ExecuteLogUtils.java b/src/com/wenshuo/agent/log/ExecuteLogUtils.java index ac2851f..a5633c6 100644 --- a/src/com/wenshuo/agent/log/ExecuteLogUtils.java +++ b/src/com/wenshuo/agent/log/ExecuteLogUtils.java @@ -20,7 +20,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import com.wenshuo.agent.AgentUtils; import com.wenshuo.agent.ConfigUtils; @@ -44,7 +43,7 @@ public class ExecuteLogUtils { private static ScheduledThreadPoolExecutor counterLogExecutor; - private static Map> executeCounterMap; + private static ConcurrentHashMap> executeCounterMap; private static final Object executeCounterLock = new Object(); @@ -82,7 +81,7 @@ public static synchronized void init() { } setNextDateStartTimeMillis(); initWriter(); - executeCounterMap = new ConcurrentHashMap>(); + executeCounterMap = new ConcurrentHashMap>(); startTimeMillis = System.currentTimeMillis(); counterLogExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("pool-thread-agent-log", true)); counterLogExecutor.scheduleWithFixedDelay(new OutputLogRunnable(), interval, interval, TimeUnit.SECONDS); @@ -99,26 +98,26 @@ public static void log(String className, String methodName, long executeTime) { * @author dingjsh * @time 2015-7-28下午01:35:25 */ - synchronized static void outputCounterLog() throws IOException { - Map> executeCounterMapInner = executeCounterMap; - executeCounterMap = new ConcurrentHashMap>(); + synchronized static void outputCounterLog() throws IOException { + ConcurrentHashMap> executeCounterMapInner = executeCounterMap; + executeCounterMap = new ConcurrentHashMap>(); String startTime = formatTimeMillis(startTimeMillis); String endTime = formatTimeMillis(System.currentTimeMillis()); long byteLength = removeJSONArrayEndBracket(); if (0 == byteLength) { - writeLog("[",true, startTimeMillis); + writeLog("[", true, startTimeMillis); } - Set>> entrySet = executeCounterMapInner.entrySet(); - Iterator>> ite = entrySet.iterator(); + Set>> entrySet = executeCounterMapInner.entrySet(); + Iterator>> ite = entrySet.iterator(); int length = entrySet.size(); if (length > 0 && byteLength > 10) { // 说明文件不只是[],json数组中已经有内容 writeLog(",", startTimeMillis); } for (int index = 0; ite.hasNext(); index++) { - Map.Entry> entry = ite.next(); + Map.Entry> entry = ite.next(); String className = entry.getKey(); - Map method2ExecuteMap = entry.getValue(); + Map method2ExecuteMap = entry.getValue(); String methodExecuteJson = MethodExecuteJSONformatter.getMethodExecuteJSON(className, method2ExecuteMap, startTime, endTime, isUsingNanoTime, logAvgExecuteTime); writeLog(methodExecuteJson, startTimeMillis); @@ -132,42 +131,35 @@ synchronized static void outputCounterLog() throws IOException { } private static void logExecuteCounter(String className, String methodName, long executeTime) { - Map methodCounterMap = getOrCreateClassExecutesMapping(className); - AtomicLong[] counter = methodCounterMap.get(methodName); + // dingjs modfied in 20210825 原来是用锁和Atomiclong来避免线程安全问题,现在去掉这些实现。原因是此处线程安全问题顶多导致 + // 数据有一些误差,这个误差对于agent数据监控并没有什么影响,牺牲一定的准确性带来性能的提升是有必要的 + ConcurrentHashMap methodCounterMap = getOrCreateClassExecutesMapping(className); + long[] counter = methodCounterMap.get(methodName); if (null == counter) { - synchronized (methodCounterMap) { - counter = methodCounterMap.get(methodName); - if (null == counter) { - methodCounterMap.put(methodName, new AtomicLong[] {new AtomicLong(1), new AtomicLong(executeTime)}); - } else { - counter[0].incrementAndGet(); - counter[1].addAndGet(executeTime); - } - } + methodCounterMap.put(methodName, new long[]{1, executeTime}); } else { - counter[0].incrementAndGet(); - counter[1].addAndGet(executeTime); + counter[0]++; + counter[1] = executeTime + counter[1]; } } /** - * * ExecuteLogUtils * - * @description 获得class和它的调用次数映射关系,如果exeuteCounterMap中没有,则创建一个并放入 * @param className 类名 * @return class和它的调用次数映射关系 + * @description 获得class和它的调用次数映射关系,如果exeuteCounterMap中没有,则创建一个并放入 * @author dingjsh * @date 2018年5月25日 下午4:40:33 * @version 1.2.0 */ - private static Map getOrCreateClassExecutesMapping(String className) { - Map methodCounterMap = executeCounterMap.get(className); + private static ConcurrentHashMap getOrCreateClassExecutesMapping(String className) { + ConcurrentHashMap methodCounterMap = executeCounterMap.get(className); if (null == methodCounterMap) { synchronized (executeCounterLock) { methodCounterMap = executeCounterMap.get(className); if (null == methodCounterMap) { - methodCounterMap = new ConcurrentHashMap(); + methodCounterMap = new ConcurrentHashMap(); executeCounterMap.put(className, methodCounterMap); } } diff --git a/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java b/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java index a886f08..13e1454 100644 --- a/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java +++ b/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java @@ -1,7 +1,7 @@ package com.wenshuo.agent.log; import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; + /** * MethodExecuteJSONformatter @@ -17,7 +17,7 @@ class MethodExecuteJSONformatter { * MethodExecuteJSONformatter * * @param className 类名 - * @param method2ExecuteMap Map<方法名, AtomicLong[]> 数组第一个是执行次数,第二个是执行时间 + * @param method2ExecuteMap Map<方法名, Long[]> 数组第一个是执行次数,第二个是执行时间 * @param startTime 统计周期的开始时间,格式为 yyyy-MM-dd HH:mm:ss * @param endTime 统计周期的结束时间,格式为 yyyy-MM-dd HH:mm:ss * @return 格式化后的json string @@ -26,19 +26,19 @@ class MethodExecuteJSONformatter { * @date 2018年8月22日 下午8:49:18 * @version 2.0.0 */ - static String getMethodExecuteJSON(String className, Map method2ExecuteMap, + static String getMethodExecuteJSON(String className, Map method2ExecuteMap, String startTime, String endTime, boolean isUsingNanoTime, boolean logAvgExecuteTime) { StringBuilder json = new StringBuilder("{"); appendString(json, "class", className).append(","); appendString(json, "start", startTime).append(","); appendString(json, "end", endTime).append(","); appendKey(json, "methods").append("["); - for (Map.Entry methodEntry : method2ExecuteMap.entrySet()) { + for (Map.Entry methodEntry : method2ExecuteMap.entrySet()) { String methodName = methodEntry.getKey(); - AtomicLong[] executeCounter = methodEntry.getValue(); - long counter = executeCounter[0].longValue(); + long[] executeCounter = methodEntry.getValue(); + long counter = executeCounter[0]; long timeInMillis - = isUsingNanoTime ? executeCounter[1].longValue() / 1000000 : executeCounter[1].longValue(); + = isUsingNanoTime ? executeCounter[1] / 1000000 : executeCounter[1]; json.append("{"); appendString(json, "name", methodName).append(","); appendLong(json, "counter", counter).append(","); From 92d6518297a98773ca97a34130aadc5adc41121c Mon Sep 17 00:00:00 2001 From: dingjs Date: Thu, 26 Aug 2021 18:47:09 +0800 Subject: [PATCH 13/25] Create LICENSE --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + 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. From d2b0f469c12973eea40edeaea66df3f9f9e0236c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=BB=BA=E6=B0=B4?= Date: Fri, 12 Aug 2022 11:42:01 +0800 Subject: [PATCH 14/25] fix p3c tips --- .../wenshuo/agent/log/ExecuteLogUtils.java | 2 +- .../AgentLogClassFileTransformer.java | 29 +++++++------------ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/com/wenshuo/agent/log/ExecuteLogUtils.java b/src/com/wenshuo/agent/log/ExecuteLogUtils.java index a5633c6..c1f9678 100644 --- a/src/com/wenshuo/agent/log/ExecuteLogUtils.java +++ b/src/com/wenshuo/agent/log/ExecuteLogUtils.java @@ -131,7 +131,7 @@ synchronized static void outputCounterLog() throws IOException { } private static void logExecuteCounter(String className, String methodName, long executeTime) { - // dingjs modfied in 20210825 原来是用锁和Atomiclong来避免线程安全问题,现在去掉这些实现。原因是此处线程安全问题顶多导致 + // dingjs modified in 20210825 原来是用锁和AtomicLong来避免线程安全问题,现在去掉这些实现。原因是此处线程安全问题顶多导致 // 数据有一些误差,这个误差对于agent数据监控并没有什么影响,牺牲一定的准确性带来性能的提升是有必要的 ConcurrentHashMap methodCounterMap = getOrCreateClassExecutesMapping(className); long[] counter = methodCounterMap.get(methodName); diff --git a/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java b/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java index 23f54a9..6343783 100644 --- a/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java +++ b/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java @@ -1,13 +1,7 @@ package com.wenshuo.agent.transformer; -import java.io.IOException; -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; -import java.security.ProtectionDomain; -import java.util.Collections; -import java.util.Set; -import java.util.regex.Pattern; - +import com.wenshuo.agent.ConfigUtils; +import com.wenshuo.agent.PojoDetector; import com.wenshuo.agent.javassist.CannotCompileException; import com.wenshuo.agent.javassist.ClassPool; import com.wenshuo.agent.javassist.CtClass; @@ -15,9 +9,12 @@ import com.wenshuo.agent.javassist.LoaderClassPath; import com.wenshuo.agent.javassist.Modifier; import com.wenshuo.agent.javassist.NotFoundException; - -import com.wenshuo.agent.ConfigUtils; -import com.wenshuo.agent.PojoDetector; +import java.io.IOException; +import java.lang.instrument.ClassFileTransformer; +import java.security.ProtectionDomain; +import java.util.Collections; +import java.util.Set; +import java.util.regex.Pattern; /** * AgentLogClassFileTransformer 类增强,增加agent日志 @@ -31,14 +28,8 @@ public class AgentLogClassFileTransformer implements ClassFileTransformer { private static final String AGENT_PACKAGE_NAME = "com.wenshuo.agent"; - /* - * (non-Javadoc) - * - * @see - * java.lang.instrument.ClassFileTransformer#transform(java.lang.ClassLoader - * , java.lang.String, java.lang.Class, java.security.ProtectionDomain, - * byte[]) - */ + + @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { byte[] byteCode = classfileBuffer; From dcb7248efabd4a9e40787a9ff3ed2364aa69e9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=BB=BA=E6=B0=B4?= Date: Mon, 16 Oct 2023 10:12:38 +0800 Subject: [PATCH 15/25] =?UTF-8?q?=E4=BF=AE=E6=94=B9javaagent=E5=8C=85?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=B8=BAjava=E9=A1=B9=E7=9B=AE=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=8C=85=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + pom.xml | 206 +- resources/JQL/JQL.py | 601 ----- resources/JQL/JQLCore.py | 107 - resources/JQL/Readme | 41 - resources/images/JQL.png | Bin 99371 -> 0 bytes src/.gitignore | 18 - src/com/wenshuo/agent/Agent.java | 26 - src/com/wenshuo/agent/AgentUtils.java | 180 -- src/com/wenshuo/agent/ConfigConsts.java | 39 - src/com/wenshuo/agent/ConfigUtils.java | 162 -- src/com/wenshuo/agent/NamedThreadFactory.java | 35 - src/com/wenshuo/agent/PojoDetector.java | 65 - .../agent/javassist/ByteArrayClassPath.java | 99 - .../javassist/CannotCompileException.java | 120 - .../agent/javassist/ClassClassPath.java | 97 - src/com/wenshuo/agent/javassist/ClassMap.java | 173 -- .../wenshuo/agent/javassist/ClassPath.java | 68 - .../wenshuo/agent/javassist/ClassPool.java | 1233 ---------- .../agent/javassist/ClassPoolTail.java | 430 ---- .../agent/javassist/CodeConverter.java | 809 ------- src/com/wenshuo/agent/javassist/CtArray.java | 113 - .../wenshuo/agent/javassist/CtBehavior.java | 1219 ---------- src/com/wenshuo/agent/javassist/CtClass.java | 1595 ------------- .../wenshuo/agent/javassist/CtClassType.java | 1761 --------------- .../agent/javassist/CtConstructor.java | 403 ---- src/com/wenshuo/agent/javassist/CtField.java | 1406 ------------ src/com/wenshuo/agent/javassist/CtMember.java | 319 --- src/com/wenshuo/agent/javassist/CtMethod.java | 438 ---- .../wenshuo/agent/javassist/CtNewClass.java | 126 -- .../agent/javassist/CtNewConstructor.java | 319 --- .../wenshuo/agent/javassist/CtNewMethod.java | 478 ---- .../agent/javassist/CtNewNestedClass.java | 67 - .../javassist/CtNewWrappedConstructor.java | 103 - .../agent/javassist/CtNewWrappedMethod.java | 199 -- .../agent/javassist/CtPrimitiveType.java | 112 - src/com/wenshuo/agent/javassist/Loader.java | 430 ---- .../agent/javassist/LoaderClassPath.java | 96 - src/com/wenshuo/agent/javassist/Modifier.java | 219 -- .../agent/javassist/NotFoundException.java | 30 - .../agent/javassist/SerialVersionUID.java | 213 -- .../wenshuo/agent/javassist/Translator.java | 71 - .../wenshuo/agent/javassist/URLClassPath.java | 179 -- .../agent/javassist/bytecode/AccessFlag.java | 134 -- .../bytecode/AnnotationDefaultAttribute.java | 160 -- .../bytecode/AnnotationsAttribute.java | 732 ------ .../javassist/bytecode/AttributeInfo.java | 303 --- .../agent/javassist/bytecode/BadBytecode.java | 40 - .../bytecode/BootstrapMethodsAttribute.java | 123 - .../agent/javassist/bytecode/ByteArray.java | 77 - .../agent/javassist/bytecode/ByteStream.java | 194 -- .../agent/javassist/bytecode/Bytecode.java | 1459 ------------ .../agent/javassist/bytecode/ClassFile.java | 932 -------- .../javassist/bytecode/ClassFilePrinter.java | 153 -- .../javassist/bytecode/ClassFileWriter.java | 791 ------- .../javassist/bytecode/CodeAnalyzer.java | 267 --- .../javassist/bytecode/CodeAttribute.java | 595 ----- .../javassist/bytecode/CodeIterator.java | 1604 ------------- .../agent/javassist/bytecode/ConstPool.java | 1995 ----------------- .../javassist/bytecode/ConstantAttribute.java | 73 - .../bytecode/DeprecatedAttribute.java | 56 - .../agent/javassist/bytecode/Descriptor.java | 872 ------- .../bytecode/DuplicateMemberException.java | 31 - .../bytecode/EnclosingMethodAttribute.java | 142 -- .../javassist/bytecode/ExceptionTable.java | 281 --- .../bytecode/ExceptionsAttribute.java | 174 -- .../agent/javassist/bytecode/FieldInfo.java | 276 --- .../bytecode/InnerClassesAttribute.java | 242 -- .../bytecode/InstructionPrinter.java | 295 --- .../bytecode/LineNumberAttribute.java | 182 -- .../bytecode/LocalVariableAttribute.java | 334 --- .../bytecode/LocalVariableTypeAttribute.java | 63 - .../agent/javassist/bytecode/LongVector.java | 64 - .../agent/javassist/bytecode/MethodInfo.java | 566 ----- .../bytecode/MethodParametersAttribute.java | 87 - .../agent/javassist/bytecode/Mnemonic.java | 239 -- .../agent/javassist/bytecode/Opcode.java | 449 ---- .../ParameterAnnotationsAttribute.java | 215 -- .../bytecode/SignatureAttribute.java | 1160 ---------- .../bytecode/SourceFileAttribute.java | 71 - .../agent/javassist/bytecode/StackMap.java | 576 ----- .../javassist/bytecode/StackMapTable.java | 1049 --------- .../bytecode/SyntheticAttribute.java | 56 - .../bytecode/TypeAnnotationsAttribute.java | 360 --- .../javassist/bytecode/analysis/Analyzer.java | 423 ---- .../bytecode/analysis/ControlFlow.java | 504 ----- .../javassist/bytecode/analysis/Executor.java | 1047 --------- .../javassist/bytecode/analysis/Frame.java | 289 --- .../bytecode/analysis/FramePrinter.java | 148 -- .../javassist/bytecode/analysis/IntQueue.java | 57 - .../bytecode/analysis/MultiArrayType.java | 130 -- .../bytecode/analysis/MultiType.java | 314 --- .../bytecode/analysis/Subroutine.java | 67 - .../bytecode/analysis/SubroutineScanner.java | 157 -- .../javassist/bytecode/analysis/Type.java | 593 ----- .../javassist/bytecode/analysis/Util.java | 48 - .../javassist/bytecode/analysis/package.html | 20 - .../bytecode/annotation/Annotation.java | 348 --- .../bytecode/annotation/AnnotationImpl.java | 305 --- .../annotation/AnnotationMemberValue.java | 96 - .../annotation/AnnotationsWriter.java | 354 --- .../bytecode/annotation/ArrayMemberValue.java | 145 -- .../annotation/BooleanMemberValue.java | 103 - .../bytecode/annotation/ByteMemberValue.java | 103 - .../bytecode/annotation/CharMemberValue.java | 104 - .../bytecode/annotation/ClassMemberValue.java | 140 -- .../annotation/DoubleMemberValue.java | 105 - .../bytecode/annotation/EnumMemberValue.java | 126 -- .../bytecode/annotation/FloatMemberValue.java | 105 - .../annotation/IntegerMemberValue.java | 110 - .../bytecode/annotation/LongMemberValue.java | 104 - .../bytecode/annotation/MemberValue.java | 89 - .../annotation/MemberValueVisitor.java | 39 - .../bytecode/annotation/NoSuchClassError.java | 40 - .../bytecode/annotation/ShortMemberValue.java | 104 - .../annotation/StringMemberValue.java | 104 - .../annotation/TypeAnnotationsWriter.java | 174 -- .../bytecode/annotation/package.html | 8 - .../agent/javassist/bytecode/package.html | 18 - .../bytecode/stackmap/BasicBlock.java | 411 ---- .../javassist/bytecode/stackmap/MapMaker.java | 606 ----- .../javassist/bytecode/stackmap/Tracer.java | 933 -------- .../javassist/bytecode/stackmap/TypeData.java | 785 ------- .../javassist/bytecode/stackmap/TypeTag.java | 30 - .../bytecode/stackmap/TypedBlock.java | 234 -- .../javassist/compiler/AccessorMaker.java | 260 --- .../agent/javassist/compiler/CodeGen.java | 1955 ---------------- .../javassist/compiler/CompileError.java | 53 - .../agent/javassist/compiler/Javac.java | 610 ----- .../agent/javassist/compiler/JvstCodeGen.java | 710 ------ .../javassist/compiler/JvstTypeChecker.java | 282 --- .../javassist/compiler/KeywordTable.java | 33 - .../wenshuo/agent/javassist/compiler/Lex.java | 551 ----- .../javassist/compiler/MemberCodeGen.java | 1160 ---------- .../javassist/compiler/MemberResolver.java | 617 ----- .../javassist/compiler/NoFieldException.java | 40 - .../agent/javassist/compiler/Parser.java | 1345 ----------- .../javassist/compiler/ProceedHandler.java | 30 - .../agent/javassist/compiler/SymbolTable.java | 45 - .../agent/javassist/compiler/SyntaxError.java | 23 - .../agent/javassist/compiler/TokenId.java | 125 -- .../agent/javassist/compiler/TypeChecker.java | 1044 --------- .../agent/javassist/compiler/ast/ASTList.java | 160 -- .../agent/javassist/compiler/ast/ASTree.java | 59 - .../javassist/compiler/ast/ArrayInit.java | 32 - .../javassist/compiler/ast/AssignExpr.java | 41 - .../agent/javassist/compiler/ast/BinExpr.java | 42 - .../javassist/compiler/ast/CallExpr.java | 47 - .../javassist/compiler/ast/CastExpr.java | 56 - .../javassist/compiler/ast/CondExpr.java | 44 - .../javassist/compiler/ast/Declarator.java | 128 -- .../javassist/compiler/ast/DoubleConst.java | 95 - .../agent/javassist/compiler/ast/Expr.java | 85 - .../javassist/compiler/ast/FieldDecl.java | 35 - .../compiler/ast/InstanceOfExpr.java | 40 - .../javassist/compiler/ast/IntConst.java | 139 -- .../agent/javassist/compiler/ast/Keyword.java | 36 - .../agent/javassist/compiler/ast/Member.java | 40 - .../javassist/compiler/ast/MethodDecl.java | 46 - .../agent/javassist/compiler/ast/NewExpr.java | 78 - .../agent/javassist/compiler/ast/Pair.java | 52 - .../agent/javassist/compiler/ast/Stmnt.java | 60 - .../agent/javassist/compiler/ast/StringL.java | 36 - .../agent/javassist/compiler/ast/Symbol.java | 36 - .../javassist/compiler/ast/Variable.java | 39 - .../agent/javassist/compiler/ast/Visitor.java | 52 - .../convert/TransformAccessArrayField.java | 270 --- .../javassist/convert/TransformAfter.java | 47 - .../javassist/convert/TransformBefore.java | 108 - .../javassist/convert/TransformCall.java | 130 -- .../convert/TransformFieldAccess.java | 82 - .../agent/javassist/convert/TransformNew.java | 103 - .../javassist/convert/TransformNewClass.java | 83 - .../javassist/convert/TransformReadField.java | 96 - .../convert/TransformWriteField.java | 72 - .../agent/javassist/convert/Transformer.java | 57 - .../wenshuo/agent/javassist/expr/Cast.java | 166 -- .../agent/javassist/expr/ConstructorCall.java | 70 - .../wenshuo/agent/javassist/expr/Expr.java | 330 --- .../agent/javassist/expr/ExprEditor.java | 317 --- .../agent/javassist/expr/FieldAccess.java | 325 --- .../wenshuo/agent/javassist/expr/Handler.java | 146 -- .../agent/javassist/expr/Instanceof.java | 170 -- .../agent/javassist/expr/MethodCall.java | 247 -- .../agent/javassist/expr/NewArray.java | 283 --- .../wenshuo/agent/javassist/expr/NewExpr.java | 249 -- .../wenshuo/agent/javassist/expr/package.html | 8 - src/com/wenshuo/agent/javassist/package.html | 22 - .../agent/javassist/runtime/Cflow.java | 53 - .../wenshuo/agent/javassist/runtime/Desc.java | 161 -- .../agent/javassist/runtime/DotClass.java | 29 - .../agent/javassist/runtime/Inner.java | 25 - .../agent/javassist/runtime/package.html | 12 - .../javassist/scopedpool/ScopedClassPool.java | 309 --- .../scopedpool/ScopedClassPoolFactory.java | 39 - .../ScopedClassPoolFactoryImpl.java | 43 - .../scopedpool/ScopedClassPoolRepository.java | 98 - .../ScopedClassPoolRepositoryImpl.java | 188 -- .../scopedpool/SoftValueHashMap.java | 233 -- .../agent/javassist/scopedpool/package.html | 7 - .../agent/javassist/tools/Callback.java | 152 -- .../wenshuo/agent/javassist/tools/Dump.java | 58 - .../agent/javassist/tools/framedump.java | 48 - .../agent/javassist/tools/package.html | 6 - .../tools/reflect/CannotCreateException.java | 30 - .../tools/reflect/CannotInvokeException.java | 69 - .../tools/reflect/CannotReflectException.java | 35 - .../tools/reflect/ClassMetaobject.java | 370 --- .../javassist/tools/reflect/Compiler.java | 163 -- .../agent/javassist/tools/reflect/Loader.java | 162 -- .../javassist/tools/reflect/Metalevel.java | 39 - .../javassist/tools/reflect/Metaobject.java | 239 -- .../javassist/tools/reflect/Reflection.java | 403 ---- .../agent/javassist/tools/reflect/Sample.java | 57 - .../javassist/tools/reflect/package.html | 35 - .../javassist/tools/rmi/AppletServer.java | 251 --- .../javassist/tools/rmi/ObjectImporter.java | 299 --- .../tools/rmi/ObjectNotFoundException.java | 27 - .../agent/javassist/tools/rmi/Proxy.java | 26 - .../javassist/tools/rmi/RemoteException.java | 31 - .../agent/javassist/tools/rmi/RemoteRef.java | 36 - .../agent/javassist/tools/rmi/Sample.java | 37 - .../javassist/tools/rmi/StubGenerator.java | 256 --- .../agent/javassist/tools/rmi/package.html | 16 - .../javassist/tools/web/BadHttpRequest.java | 35 - .../agent/javassist/tools/web/Viewer.java | 209 -- .../agent/javassist/tools/web/Webserver.java | 408 ---- .../agent/javassist/tools/web/package.html | 7 - .../agent/javassist/util/HotSwapper.java | 251 --- .../wenshuo/agent/javassist/util/package.html | 5 - .../javassist/util/proxy/FactoryHelper.java | 237 -- .../javassist/util/proxy/MethodFilter.java | 31 - .../javassist/util/proxy/MethodHandler.java | 49 - .../agent/javassist/util/proxy/Proxy.java | 33 - .../javassist/util/proxy/ProxyFactory.java | 1446 ------------ .../javassist/util/proxy/ProxyObject.java | 44 - .../util/proxy/ProxyObjectInputStream.java | 100 - .../util/proxy/ProxyObjectOutputStream.java | 72 - .../javassist/util/proxy/RuntimeSupport.java | 271 --- .../javassist/util/proxy/SecurityActions.java | 136 -- .../javassist/util/proxy/SerializedProxy.java | 99 - .../agent/javassist/util/proxy/package.html | 6 - .../wenshuo/agent/log/ExecuteLogUtils.java | 316 --- .../agent/log/MethodExecuteJSONformatter.java | 70 - .../wenshuo/agent/log/OutputLogRunnable.java | 31 - .../AgentLogClassFileTransformer.java | 156 -- src/props/agent.properties | 16 - test/com/wenshuo/agent/test/TestCtMethod.java | 50 - 248 files changed, 20 insertions(+), 63340 deletions(-) delete mode 100644 resources/JQL/JQL.py delete mode 100644 resources/JQL/JQLCore.py delete mode 100644 resources/JQL/Readme delete mode 100644 resources/images/JQL.png delete mode 100644 src/.gitignore delete mode 100644 src/com/wenshuo/agent/Agent.java delete mode 100644 src/com/wenshuo/agent/AgentUtils.java delete mode 100644 src/com/wenshuo/agent/ConfigConsts.java delete mode 100644 src/com/wenshuo/agent/ConfigUtils.java delete mode 100644 src/com/wenshuo/agent/NamedThreadFactory.java delete mode 100644 src/com/wenshuo/agent/PojoDetector.java delete mode 100644 src/com/wenshuo/agent/javassist/ByteArrayClassPath.java delete mode 100644 src/com/wenshuo/agent/javassist/CannotCompileException.java delete mode 100644 src/com/wenshuo/agent/javassist/ClassClassPath.java delete mode 100644 src/com/wenshuo/agent/javassist/ClassMap.java delete mode 100644 src/com/wenshuo/agent/javassist/ClassPath.java delete mode 100644 src/com/wenshuo/agent/javassist/ClassPool.java delete mode 100644 src/com/wenshuo/agent/javassist/ClassPoolTail.java delete mode 100644 src/com/wenshuo/agent/javassist/CodeConverter.java delete mode 100644 src/com/wenshuo/agent/javassist/CtArray.java delete mode 100644 src/com/wenshuo/agent/javassist/CtBehavior.java delete mode 100644 src/com/wenshuo/agent/javassist/CtClass.java delete mode 100644 src/com/wenshuo/agent/javassist/CtClassType.java delete mode 100644 src/com/wenshuo/agent/javassist/CtConstructor.java delete mode 100644 src/com/wenshuo/agent/javassist/CtField.java delete mode 100644 src/com/wenshuo/agent/javassist/CtMember.java delete mode 100644 src/com/wenshuo/agent/javassist/CtMethod.java delete mode 100644 src/com/wenshuo/agent/javassist/CtNewClass.java delete mode 100644 src/com/wenshuo/agent/javassist/CtNewConstructor.java delete mode 100644 src/com/wenshuo/agent/javassist/CtNewMethod.java delete mode 100644 src/com/wenshuo/agent/javassist/CtNewNestedClass.java delete mode 100644 src/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java delete mode 100644 src/com/wenshuo/agent/javassist/CtNewWrappedMethod.java delete mode 100644 src/com/wenshuo/agent/javassist/CtPrimitiveType.java delete mode 100644 src/com/wenshuo/agent/javassist/Loader.java delete mode 100644 src/com/wenshuo/agent/javassist/LoaderClassPath.java delete mode 100644 src/com/wenshuo/agent/javassist/Modifier.java delete mode 100644 src/com/wenshuo/agent/javassist/NotFoundException.java delete mode 100644 src/com/wenshuo/agent/javassist/SerialVersionUID.java delete mode 100644 src/com/wenshuo/agent/javassist/Translator.java delete mode 100644 src/com/wenshuo/agent/javassist/URLClassPath.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/AccessFlag.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/BadBytecode.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ByteArray.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ByteStream.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/Bytecode.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ClassFile.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/CodeIterator.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ConstPool.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/Descriptor.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/FieldInfo.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/LongVector.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/MethodInfo.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/Mnemonic.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/Opcode.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/StackMap.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/StackMapTable.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/Type.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/Util.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/analysis/package.html delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/annotation/package.html delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/package.html delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java delete mode 100644 src/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/AccessorMaker.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/CodeGen.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/CompileError.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/Javac.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/KeywordTable.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/Lex.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/MemberResolver.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/NoFieldException.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/Parser.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ProceedHandler.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/SymbolTable.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/SyntaxError.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/TokenId.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/TypeChecker.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/ASTList.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/ASTree.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/Declarator.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/Expr.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/IntConst.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/Keyword.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/Member.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/Pair.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/StringL.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/Symbol.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/Variable.java delete mode 100644 src/com/wenshuo/agent/javassist/compiler/ast/Visitor.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/TransformAfter.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/TransformBefore.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/TransformCall.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/TransformNew.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/TransformNewClass.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/TransformReadField.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/TransformWriteField.java delete mode 100644 src/com/wenshuo/agent/javassist/convert/Transformer.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/Cast.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/ConstructorCall.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/Expr.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/ExprEditor.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/FieldAccess.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/Handler.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/Instanceof.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/MethodCall.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/NewArray.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/NewExpr.java delete mode 100644 src/com/wenshuo/agent/javassist/expr/package.html delete mode 100644 src/com/wenshuo/agent/javassist/package.html delete mode 100644 src/com/wenshuo/agent/javassist/runtime/Cflow.java delete mode 100644 src/com/wenshuo/agent/javassist/runtime/Desc.java delete mode 100644 src/com/wenshuo/agent/javassist/runtime/DotClass.java delete mode 100644 src/com/wenshuo/agent/javassist/runtime/Inner.java delete mode 100644 src/com/wenshuo/agent/javassist/runtime/package.html delete mode 100644 src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java delete mode 100644 src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java delete mode 100644 src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java delete mode 100644 src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java delete mode 100644 src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java delete mode 100644 src/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java delete mode 100644 src/com/wenshuo/agent/javassist/scopedpool/package.html delete mode 100644 src/com/wenshuo/agent/javassist/tools/Callback.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/Dump.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/framedump.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/package.html delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/Compiler.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/Loader.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/Reflection.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/Sample.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/reflect/package.html delete mode 100644 src/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/rmi/Proxy.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/rmi/Sample.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/rmi/package.html delete mode 100644 src/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/web/Viewer.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/web/Webserver.java delete mode 100644 src/com/wenshuo/agent/javassist/tools/web/package.html delete mode 100644 src/com/wenshuo/agent/javassist/util/HotSwapper.java delete mode 100644 src/com/wenshuo/agent/javassist/util/package.html delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/Proxy.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java delete mode 100644 src/com/wenshuo/agent/javassist/util/proxy/package.html delete mode 100644 src/com/wenshuo/agent/log/ExecuteLogUtils.java delete mode 100644 src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java delete mode 100644 src/com/wenshuo/agent/log/OutputLogRunnable.java delete mode 100644 src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java delete mode 100644 src/props/agent.properties delete mode 100644 test/com/wenshuo/agent/test/TestCtMethod.java diff --git a/.gitignore b/.gitignore index 1dd8028..3a8beb1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.jar *.war *.ear +javaagent.iml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* diff --git a/pom.xml b/pom.xml index 4e771ad..4bc2299 100644 --- a/pom.xml +++ b/pom.xml @@ -4,12 +4,17 @@ com.wenshuo javaagent - 2.1.1 + 2.1.2 jar + javaagent + 基于javaagent技术来记录方法执行次数和时间 + https://github.com/dingjs/javaagent + UTF-8 1.6 + UTF-8 @@ -27,203 +32,30 @@ - src - target/classes - - - src - - **/*.java - - - - - test - - - test - - **/*.java - - - org.apache.maven.plugins maven-jar-plugin - - - process-classes - - jar - - - - - com.wenshuo.agent.Agent - - - - - + 3.3.0 + + + + com.wenshuo.agent.Agent + + + org.apache.maven.plugins maven-compiler-plugin - - - org.codehaus.mojo - buildnumber-maven-plugin - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - org.codehaus.mojo - sonar-maven-plugin + + 6 + 6 + - - - - org.codehaus.mojo - sonar-maven-plugin - 2.0 - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - ${project.build.target} - ${project.build.target} - ${project.build.sourceEncoding} - - - - org.apache.maven.plugins - maven-dependency-plugin - 2.1 - - false - runtime - true - false - false - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.4 - - - org.apache.maven.plugins - maven-source-plugin - 2.0.4 - - true - - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - default-jar - - - - - - - - true - - - ${buildNumber} - ${project.build.target} - - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.3 - - - - ${project.version} - ${project.artifactId} - - true - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.4.3 - - - org.apache.maven.plugins - maven-help-plugin - 2.0 - - - org.apache.maven.plugins - maven-install-plugin - 2.2 - - - org.apache.maven.plugins - maven-clean-plugin - 2.3 - - - org.codehaus.mojo - buildnumber-maven-plugin - 1.0-beta-2 - - - validate - - create - - - - - {0,date,yyyy-MM-dd HH:mm:ss} - - timestamp - - - - - org.apache.maven.plugins - maven-shade-plugin - 1.4 - - - - com.sun @@ -236,7 +68,7 @@ junit junit - 4.12 + 4.13.2 test diff --git a/resources/JQL/JQL.py b/resources/JQL/JQL.py deleted file mode 100644 index d1210df..0000000 --- a/resources/JQL/JQL.py +++ /dev/null @@ -1,601 +0,0 @@ -#coding=utf-8 -import re, io, os, sys, time -from JQLCore import AgentLogTableTitles, ColumnDefinition, QueryDefinition -from JQLCore import CompareItem, JQL_COMPARE, SortItem - - -START_TIME_PATTERN = re.compile(r"startTime:{.*}") -END_TIME_PATTERN = re.compile(r"endTime:{.*}") -CLASS_NAME_PATTERN = re.compile(r"className:{.*}") -METHOD_NAME_PATTERN = re.compile(r"methodName:.*") -COUNTER_PATTERN = re.compile(r"counter.*") -TIME_PATTERN = re.compile(r"time.*") -EARLIEST_TIME = time.strptime('1970-01-01 12:00:00', '%Y-%m-%d %H:%M:%S') -JQL_SPLIT_KEY = r"select|from|where|order by|limit|;" -ORDER_PATTERN = re.compile(r"asc|desc") - -def checkJQL(jql): - """校验jql - - 校验规则: - 1. 关键字顺序必须为:select, from, where, order by, limit - 2. from为必填项 - """ - - if len(jql.strip()) == 0: - raise Exception("JQL empty error") - - max = -1 - pos = jql.find("select") - if pos != -1: - if pos > max: - max = pos - else: - raise Exception('select order error') - - pos = jql.find("from") - if pos != -1: - if pos > max: - max = pos - else: - raise Exception('from order error') - else: - raise Exception('no from error') - - pos = jql.find("where") - if pos != -1: - if pos > max: - max = pos - else: - raise Exception('where order error') - - pos = jql.find("order by") - if pos != -1: - if pos > max: - max = pos - else: - raise Exception('order by order error') - - pos = jql.find("limit") - if pos != -1: - if pos > max: - max = pos - else: - raise Exception('limit order error') - -def parseJQL(jql): - """解析查询语句 - - Args: - jql: 查询语句 - Return: - QueryDefinition: 解析查询语句得到的定义 - """ - - #分片 - segments = re.split(JQL_SPLIT_KEY, jql) - - i = 1 - #处理select - titleList = [] - if jql.find("select") != -1: - selectStr = segments[i].strip() - i += 1 - - if len(selectStr) == 0: - raise Exception('select no values error') - - for title in selectStr.split(","): - title = title.strip() - - if len(title) == 0: - raise Exception('selct empty value error: select ' + selectStr) - - if title not in AgentLogTableTitles: - raise Exception('select value error:\'' + title + '\'') - - titleList.append(title) - else: - # 默认表头 - titleList = AgentLogTableTitles - - #处理from - fileList = [] - if jql.find("from") != -1: - fromStr = segments[i].strip() - i += 1 - - if len(fromStr) == 0: - raise Exception('from no values error') - - for root in fromStr.split(","): - root = root.strip() - if len(root) == 0: - raise Exception('from empty value error: where ' + fromStr) - - if os.path.isdir(root): - files = os.listdir(root) - for file in files: - fileList.append(os.path.join(root, file)) - elif os.path.isfile(root): - fileList.append(root) - else: - raise Exception('from value error: ' + root + ' is not a file or dir') - else: - raise Exception('no from error') - - #处理where - encoding = "utf-8" - compareList = [] - if jql.find("where") != -1: - whereStr = segments[i].strip() - i += 1 - - if len(whereStr) == 0: - raise Exception('where no values error') - - #得到文件编码、过滤条件 - filterList = [] - for param in whereStr.split("and"): - elements = param.split('=') - key = elements[0].strip() - if key == "encoding": - encoding = elements[1].strip() - else: - filterList.append(param) - - #根据过滤得到比较列表 - for filter in filterList: - operator = None - if filter.find(">=") != -1: - operator = ">=" - elif filter.find("<=") != -1: - operator = "<=" - elif filter.find("=") != -1: - operator = "=" - elif filter.find(">") != -1: - operator = ">" - elif filter.find("<") != -1: - operator = "<" - else: - continue - - elements = filter.split(operator) - if len(elements) != 2: - continue - - ci = CompareItem(elements[0].strip(), operator, elements[1].strip()) - compareList.append(ci) - - #处理order by - keyList = [] - orderList = [] - if jql.find("order by") != -1: - orderByStr = segments[i].strip() - i += 1 - - if len(fromStr) == 0: - raise Exception('order by no values error') - - for order in orderByStr.split(","): - order = order.strip() - - if len(order) == 0: - raise Exception('order by empty values error') - else: - pos = re.search(ORDER_PATTERN, order) - key = None - keyOrder = None - if pos is None: - # 默认升序 - key = order - keyOrder = "asc" - else: - key = order[: pos.start()].strip() - keyOrder = order[pos.start() : pos.end()] - - if key not in AgentLogTableTitles: - raise Exception('order by no support key error. key = ' + key) - - keyList.append(key) - orderList.append(keyOrder) - - #处理limit - limit = -1 - if jql.find("limit") != -1: - limitStr = segments[i].strip() - i += 1 - - if len(limitStr) == 0: - raise Exception('limit no values error') - - if limitStr.isdigit() == False: - raise Exception("limit value type error, value must be a nonnegative integer , value = " + limitStr) - - limit = limitStr - - return QueryDefinition(titleList, fileList, encoding, keyList, orderList, compareList, limit) - -def getValueInBrackets(str): - """得到括号内的值 - - Args: - str: 包括{}的字符串 - - Return: - {}内的值(去除了2端的空格) - """ - - return getValueBetweenKey1AndKey2(str, "{", "}") - -def getValueBetweenKey1AndKey2(str, key1, key2): - """得到关键字1和关键字2之间的值 - - Args: - str: 包括key1、key2的字符串 - key1: 关键字1 - key2: 关键字2 - - Return: - key1 ... key2 内的值(去除了2端的空格) - """ - - offset = len(key1) - start = str.find(key1) + offset - end = str.find(key2) - value = str[start : end] - return value.strip() - -def queryDatas(queryDef): - """查询并过滤数据 - - Args: - queryDef: 查询定义 - Return: - SortItem List:待排序的数据列表 - """ - - result = [] - #查询所有文件的数据 - for file in queryDef.fileList: - temp = [] - startTime = None - clazz = None - method = None - count = 0 - totalTime = 0 - avgTime = 0 - with io.open(file, 'r', encoding=queryDef.encoding) as fd: - for line in fd: - #处理startTime:...行 - match = START_TIME_PATTERN.match(line) - if match: - #如果开始时间有变化,则保存结果,否则丢弃 - newStartTime = getValueInBrackets(line) - if newStartTime != startTime: - result.extend(temp) - startTime = newStartTime - temp = [] - continue - - #处理endTime:...行 - match = END_TIME_PATTERN.match(line) - if match: - endTime = getValueInBrackets(line) - for item in temp: - item.endTime = endTime - continue - - #解析className...行 - match = CLASS_NAME_PATTERN.match(line) - if match: - clazz = getValueInBrackets(line) - continue - - #解析methodName...行 - match = METHOD_NAME_PATTERN.match(line) - if match: - for str in line.split(','): - #处理methodName - match = METHOD_NAME_PATTERN.match(str) - if match: - method = getValueInBrackets(str) - continue - - #处理counter - match = COUNTER_PATTERN.match(str) - if match: - count = getValueInBrackets(str) - continue - - #处理time - match = TIME_PATTERN.match(str) - if match: - totalTime = getValueInBrackets(str) - continue - - item = SortItem(clazz, method, count, totalTime, startTime) - temp.append(item) - - #保存最后一个时间域的数据 - result.extend(temp) - - return result - -def filterDatas(itemList, queryDef): - """过滤数据列表 - - Args: - itemList: 数据 - queryDef: 查询定义 - Return: - dataList: 过滤后的数据 - """ - - dataList = [] - for item in itemList: - if checkItem(item, queryDef): - dataList.append(item) - - return dataList - -def checkItem(item, queryDef): - """校验数据 - - Args: - item: 数据 - queryDef: 查询定义 - """ - - result = True - for ci in queryDef.compareList: - if ci.key == "time": - result = JQL_COMPARE.get(ci.operator)(item.time, int(ci.value)) - elif ci.key == "count": - result = JQL_COMPARE.get(ci.operator)(item.count, int(ci.value)) - elif ci.key == "avg time": - result = JQL_COMPARE.get(ci.operator)(item.avgTime, int(ci.value)) - elif ci.key == "method": - result = JQL_COMPARE.get(ci.operator)(item.method.lower(), ci.value) - elif ci.key == "class": - result = JQL_COMPARE.get(ci.operator)(item.clazz.lower(), ci.value) - elif ci.key == "start time": - result = JQL_COMPARE.get(ci.operator)( - time.strptime(item.startTime, '%Y-%m-%d %H:%M:%S'), - time.strptime(ci.value, '%Y-%m-%d %H:%M:%S') - ) - elif ci.key == "end time": - result = JQL_COMPARE.get(ci.operator)( - time.strptime(item.endTime, '%Y-%m-%d %H:%M:%S'), - time.strptime(ci.value, '%Y-%m-%d %H:%M:%S') - ) - - if result == False: - break - - return result - -def sortAndCut(itemList, queryDef): - """将itemList根据查询条件排序 - - Args: - itemList: 数据 - queryDef:查询定义 - Return: - dataList: 排序和过滤后的结果 - """ - - #处理排序 - for key, order in zip(reversed(queryDef.keyList), reversed(queryDef.orderList)): - #得到lambda函数 - keyLambda = None - if key == 'count': - keyLambda = lambda item: item.count - elif key == 'time': - keyLambda = lambda item: item.time - elif key == 'avg time': - keyLambda = lambda item: item.avgTime - elif key == 'start time': - keyLambda = lambda item: time.strptime(item.startTime, '%Y-%m-%d %H:%M:%S') if item.startTime != None else EARLIEST_TIME - elif key == 'end time': - keyLambda = lambda item: time.strptime(item.endTime, '%Y-%m-%d %H:%M:%S') if item.endTime != None else EARLIEST_TIME - else: - continue - - #判断是否逆排序(默认递增排序) - rev = (order == 'desc') - - itemList.sort(key=keyLambda, reverse=rev) - - return itemList[0 : queryDef.limit] if queryDef.limit != -1 else itemList - -def drawTable(itemList, queryDef): - """画表格 - - Args: - itemList: 数据 - queryDef: 查询定义 - - Example: - +---------+----------+-----------+------------+--------------+ - | count | time | avg time | method | class | - +---------+----------+-----------+------------+--------------+ - | 10 | 500 | 50 | sayHello | Test.java | - +---------+----------+-----------+------------+--------------+ - """ - - # 画表头 - drawLine(queryDef) - drawTitle(queryDef) - drawLine(queryDef) - - # 画数据行 - drawDataRows(itemList, queryDef) - - # 画结尾行 - drawLine(queryDef) - -def drawLine(queryDef): - """画分割线 - - Args: - queryDef: 查询定义 - """ - - formatStr = "" - valueList = [] - for title in queryDef.titleList: - colDef = queryDef.tableCols[title] - size = colDef.size + 2 - formatStr += ('+' + formatString("string", size, False)) - valueList.append("-"*size) - - formatStr += "+" - - print(formatStr % tuple(valueList)) - -def drawTitle(queryDef): - """画表头 - - Args: - queryDef: 查询定义 - """ - - formatStr = "" - valueList = [] - for title in queryDef.titleList: - colDef = queryDef.tableCols[title] - formatStr += ('| ' + formatString("string", colDef.size) + ' ') - valueList.append(title) - - formatStr += "|" - print(formatStr % tuple(valueList)) - -def drawDataRows(dataList, queryDef): - """画数据行 - - Args: - itemList: 数据 - queryDef: 查询定义 - """ - - for data in dataList: - formatStr = "" - valueList = [] - for title in queryDef.titleList: - colDef = queryDef.tableCols[title] - formatStr += ('| ' + formatString(colDef.type, colDef.size) + ' ') - - if title == 'time': - valueList.append(data.time) - elif title == 'count': - valueList.append(data.count) - elif title == 'avg time': - valueList.append(data.avgTime) - elif title == 'method': - valueList.append(data.method) - elif title == 'class': - valueList.append(data.clazz) - elif title == 'start time': - valueList.append(data.startTime) - elif title == 'end time': - valueList.append(data.endTime) - - formatStr += "|" - print(formatStr % tuple(valueList)) - -def formatString(type, size, leftAlignment=True): - """得到格式化占位符 - - Args: - type: 数据类型 - size: 位数 - leftAlignment:是否左对齐 - - Return: - 格式化占位符 - """ - - prefix = ('-' if leftAlignment else '') - result = '%' + prefix + str(size) - - if type == "int": - result += 'd' - elif type == "float": - result += '.2f' - else: - result += 's' - - return result - -def executeQuery(jql): - """执行jql,并输出表格 - - Args: - jql: 查询语句 - """ - - try: - print("") - - #校验jql - none, checkTime = JQL_TIMER(checkJQL, jql) - #解析jql - queryDef, parseTime = JQL_TIMER(parseJQL, jql) - #查询数据 - dataList, queryTime = JQL_TIMER(queryDatas, queryDef) - #过滤数据 - dataList, filterTime = JQL_TIMER(filterDatas, dataList, queryDef) - #排序、裁剪数据 - dataList, sortTime = JQL_TIMER(sortAndCut, dataList, queryDef) - #画表 - none, drawTime = JQL_TIMER(drawTable, dataList, queryDef) - - total = checkTime + parseTime + queryTime + filterTime + sortTime + drawTime - - print("spend %.2fs(check: %.2fs, parse: %.2fs, query: %.2fs, filter: %.2fs, sort: %.2fs, draw: %.2fs)\n" % - (total, checkTime, parseTime, queryTime, filterTime, sortTime, drawTime)) - except Exception as err: - msg = "[JQL] {0}".format(err) - length = len(msg) - print("%s\n%s\n%s\n" % ("*"*length, msg, "*"*length)) - -def JQL_TIMER(func, *args): - """计时函数 - - Args: - func: 函数名 - args: 可变参数 - Return: - 函数执行结果, 函数执行时间 - """ - - start = time.time() - result = func(*args) - end = time.time() - return result, end - start - -def main(): - """主函数""" - - if len(sys.argv) > 1: - executeQuery(sys.argv[1]) - else: - print("Please input JQL\ninput 'q!' for quit, input ';' for execute jql\n") - jql = "" - while (True): - line = input(">>> ").lower() - if (line == "q!"): - print("Thanks.") - sys.exit() - elif line.find(";") != -1: - line = line.split(";") - jql += (' ' + line[0].strip() + ';') - executeQuery(jql) - jql = "" - else: - jql += (' ' + line.strip()) - -if __name__ == "__main__": - main() - diff --git a/resources/JQL/JQLCore.py b/resources/JQL/JQLCore.py deleted file mode 100644 index 327367b..0000000 --- a/resources/JQL/JQLCore.py +++ /dev/null @@ -1,107 +0,0 @@ -#coding=utf-8 - -class ColumnDefinition: - """列定义""" - - def __init__(self, name, type, size): - self.name = name - self.type = type - self.size = int(size) - -#java-agent-log-table表头定义 -AgentLogTableTitles = ['time', 'count', 'avg time', 'method', 'class', 'start time', 'end time'] - -#java-agent-log-table定义 -AgentLogTable = { - 'time': ColumnDefinition('time', 'int', 10), - 'count': ColumnDefinition('count', 'int', 10), - 'avg time': ColumnDefinition('avg time', 'int', 10), - 'method': ColumnDefinition('method', 'string', 35), - 'class': ColumnDefinition('method', 'string', 100), - 'start time': ColumnDefinition('start time', 'string', 20), - 'end time': ColumnDefinition('end time', 'string', 20) -} - -class QueryDefinition: - """查询定义 - - 将解析后的查询条件保存于此,用于查询方法间的参数传递 - """ - - def __init__(self, titleList, fileList, encoding, keyList, orderList=[], compareList=[], limit=-1): - self.titleList = titleList - self.fileList = fileList - self.encoding = encoding - self.keyList = keyList - self.orderList = orderList - self.compareList = compareList - self.limit = int(limit) - self.tableCols= AgentLogTable - -class CompareItem: - """比较项""" - - def __init__(self, key, operator, value): - self.key = key - self.operator = operator - self.value = value - -def equal(x, y): - return x == y - -def lessThan(x, y): - return x < y - -def lessThanOrEqualTo(x, y): - return x <= y - -def greaterThan(x, y): - return x > y - -def greaterThanOrEqualTo(x, y): - return x >= y - -#jql比较操作 -JQL_COMPARE = { - "=": equal, - "<": lessThan, - "<=": lessThanOrEqualTo, - ">": greaterThan, - ">=": greaterThanOrEqualTo -} - -class SortItem: - """排序项""" - - def __init__(self, clazz, method, count, time, startTime, endTime=None): - """ - Args: - clazz: 类名 - method: 方法 - count: 方法调用总次数 - time: 方法调用总时间 - startTime: 开始时间 - """ - - self.clazz = clazz - self.method = method - self.count = int(count) - self.time = int(time) - self.startTime = startTime - self.endTime = endTime - self.avgTime = float(self.time / self.count) #方法调用平均时间 - - def copy(self): - """复制""" - - return SortItem(self.clazz, self.method, self.count, self.time, self.startTime) - - def equals(self, other): - """比较是否相等""" - - return (self.clazz == other.clazz - and self.method == other.method - and self.count == other.count - and self.time == other.time - and self.startTime == other.startTime - and self.avgTime == other.avgTime) diff --git a/resources/JQL/Readme b/resources/JQL/Readme deleted file mode 100644 index 1e7b013..0000000 --- a/resources/JQL/Readme +++ /dev/null @@ -1,41 +0,0 @@ -JQL工具由北京华宇信息技术有限公司-金雷(人送外号金大侠)开发。 - -[目录说明] -/JQL -- 代码目录 - JQL.py -- 实现JQL查询 - JQLCore.py -- JQL核心数据结构定义 - - -[使用说明] -1. 确保python34安装完毕,并且在环境变量里配置了其path -2. 进入JQL目录,空白处按住shift+右键,选择"在"在此处打开命令窗口",弹出命令窗口 -3. 在命令窗口输入jql,进入JQL程序,如果看到如下字符串表示启动成功 -Please input JQL -input 'q!' for quit, input ';' for execute jql - ->>>_ -4. 打开JQL例子.txt,随意复制一个jql查询语句进去,按回车,JQL将返回一个表格做为结构 - -[JQL语法说明] -[ select < col1 [ , col2, col3, ... ] > ] -< from < file1|folder1 [ , file2|folder2, file3|folder3, ... ] > > -[ where ] -[ order by ] -[ limit ] - -补充说明: -from里如果是文件夹,则统计文件夹下所有文件 -where条件目前只支持and -如果表格看起来很混乱,需要右击命令窗口 > 属性 > 布局, 将屏幕缓冲区大小调大点即可,我的参数是 宽度:3000 * 高度 6000 - -[版本说明] -JQL0.1Beta刚刚诞生,目前非常不成熟,还只支持一种log:即丁建水的thunisoft-javaagent产生的日志 - -[后续工作] -0.2版:计划支持所有主流log,where条件支持同select -0.3版:计划支持C/S结构 -0.4版:计划支持主-主结构,主-从结构 -0.5版:计划用QT实现一个界面 -1.0版:系统稳定,正式发版 - -1.0版发布后,可能实现身份证系统识别,也可能鸽了,看心情 diff --git a/resources/images/JQL.png b/resources/images/JQL.png deleted file mode 100644 index 5f982a88632ec656426a41b84a3f51cf42479ab7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99371 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 CzxlZU diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index 5e9ac25..0000000 --- a/src/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -target/maven-archiver/pom.properties -*.prefs -*.project -.classpath -/target -/.settings diff --git a/src/com/wenshuo/agent/Agent.java b/src/com/wenshuo/agent/Agent.java deleted file mode 100644 index 1bfd12b..0000000 --- a/src/com/wenshuo/agent/Agent.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * @(#)Agent.java 2015-7-24 上午09:49:34 - * javaagent - * Copyright 2015 wenshuo, Inc. All rights reserved. - * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ -package com.wenshuo.agent; -import java.lang.instrument.Instrumentation; -import com.wenshuo.agent.log.ExecuteLogUtils; -import com.wenshuo.agent.transformer.AgentLogClassFileTransformer; - -/** - * Agent - * - * @author dingjsh - */ -public class Agent { - - public static void premain(String agentArs, Instrumentation inst) { - // 初始化配置 - ConfigUtils.initProperties(agentArs); - ExecuteLogUtils.init(); - inst.addTransformer(new AgentLogClassFileTransformer()); - } - -} diff --git a/src/com/wenshuo/agent/AgentUtils.java b/src/com/wenshuo/agent/AgentUtils.java deleted file mode 100644 index e133898..0000000 --- a/src/com/wenshuo/agent/AgentUtils.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * @(#)AgentUtils.java 2015-7-27 下午05:26:24 - * javaagent - * Copyright 2015 wenshuo, Inc. All rights reserved. - * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ -package com.wenshuo.agent; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Writer; - -/** - * AgentUtils - * - * @author dingjsh - * @time 2015-7-27下午05:26:24 - */ -public class AgentUtils { - - /** - *

- * Checks if a String is whitespace, empty ("") or null. - *

- * - *
-     * StringUtils.isBlank(null)      = true
-     * StringUtils.isBlank("")        = true
-     * StringUtils.isBlank(" ")       = true
-     * StringUtils.isBlank("bob")     = false
-     * StringUtils.isBlank("  bob  ") = false
-     * 
- * - * @param str the String to check, may be null - * @return true if the String is null, empty or whitespace - * @since 2.0 - */ - public static boolean isBlank(String str) { - int strLen; - if (str == null || (strLen = str.length()) == 0) { - return true; - } - for (int i = 0; i < strLen; i++) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Checks if a String is not empty (""), not null and not whitespace only. - *

- * - *
-     * StringUtils.isNotBlank(null)      = false
-     * StringUtils.isNotBlank("")        = false
-     * StringUtils.isNotBlank(" ")       = false
-     * StringUtils.isNotBlank("bob")     = true
-     * StringUtils.isNotBlank("  bob  ") = true
-     * 
- * - * @param str the String to check, may be null - * @return true if the String is not empty and not null and not - * whitespace - * @since 2.0 - */ - static boolean isNotBlank(String str) { - return !isBlank(str); - } - - /** - * Unconditionally close an InputStream. - *

- * Equivalent to {@link InputStream#close()}, except any exceptions will be - * ignored. This is typically used in finally blocks. - * - * @param input the InputStream to close, may be null or already closed - */ - static void closeQuietly(InputStream input) { - try { - if (input != null) { - input.close(); - } - } catch (IOException ioe) { - // ignore - } - } - - public static int copy(InputStream input, OutputStream output) - throws IOException { - long count = copyLarge(input, output); - if (count > Integer.MAX_VALUE) { - return -1; - } - return (int) count; - } - - private static long copyLarge(InputStream input, OutputStream output) - throws IOException { - byte[] buffer = new byte[4096]; - long count = 0; - int n; - while (-1 != (n = input.read(buffer))) { - output.write(buffer, 0, n); - count += n; - } - return count; - } - - // read toByteArray - //----------------------------------------------------------------------- - - /** - * Get the contents of an InputStream as a byte[]. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from - * @return the requested byte array - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static byte[] toByteArray(InputStream input) throws IOException { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - copy(input, output); - return output.toByteArray(); - } - - /** - * Unconditionally close a Writer. - *

- * Equivalent to {@link Writer#close()}, except any exceptions will be ignored. - * This is typically used in finally blocks. - * - * @param output the Writer to close, may be null or already closed - */ - public static void closeQuietly(Writer output) { - try { - if (output != null) { - output.close(); - } - } catch (IOException ioe) { - // ignore - } - } - - /** - * Makes a directory, including any necessary but nonexistent parent - * directories. If there already exists a file with specified name or - * the directory cannot be created then an exception is thrown. - * - * @param directory directory to create, must not be null - * @throws NullPointerException if the directory is null - * @throws IOException if the directory cannot be created - */ - public static void forceMkdir(File directory) throws IOException { - if (directory.exists()) { - if (directory.isFile()) { - String message = - "File " - + directory - + " exists and is " - + "not a directory. Unable to create directory."; - throw new IOException(message); - } - } else { - if (!directory.mkdirs()) { - String message = - "Unable to create directory " + directory; - throw new IOException(message); - } - } - } -} diff --git a/src/com/wenshuo/agent/ConfigConsts.java b/src/com/wenshuo/agent/ConfigConsts.java deleted file mode 100644 index 6edd2c4..0000000 --- a/src/com/wenshuo/agent/ConfigConsts.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * @(#)ConfigConsts.java 2015-7-27 下午06:06:23 - * javaagent - * Copyright 2015 wenshuo, Inc. All rights reserved. - * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ -package com.wenshuo.agent; - -/** - * ConfigConsts - * - * @author dingjsh - * @time 2015-7-27下午06:06:23 - */ -public interface ConfigConsts { - - String EXCLUDE_PACKAGE_DEFAULT = "agent.exclude.package.default"; - - String EXCLUDE_PACKAGE = "agent.exclude.package"; - - String INCLUDE_PACKAGE = "agent.include.package"; - - String LOG_FILE = "agent.log.file"; - - String LOG_INTERVAL_SECONDS = "agent.log.interval.seconds"; - - String EXCLUDE_CLASS_REGEX = "agent.exclude.class.regex"; - - String EXCLUDE_CLASS_REGEX_DEFAULT = "agent.exclude.class.regex.default"; - - String LOG_AVG_EXECUTE_TIME = "agent.log.avg.execute.time"; - - String POJO_MONITOR_OPEN = "agent.pojo.monitor.open"; - - String LOG_TIME_NANO = "agent.log.nano"; - - int DEFAULT_LOG_INTERVAL = 600; - -} diff --git a/src/com/wenshuo/agent/ConfigUtils.java b/src/com/wenshuo/agent/ConfigUtils.java deleted file mode 100644 index 4de6036..0000000 --- a/src/com/wenshuo/agent/ConfigUtils.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.wenshuo.agent; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Properties; -import java.util.Set; - -/** - * 获取配置信息
- * ConfigUtils - * - * @author dingjsh - * @time 2015-7-27下午09:16:18 - */ -public class ConfigUtils { - - private static Properties props; - - private static Set excludePackages; - - private static Set includePackages; - - private static Set excludeClassRegexs; - - private ConfigUtils() { - super(); - } - - static void initProperties(String propertiesFileName) { - props = getProperties(propertiesFileName); - initExcluePackages(); - initIncludePackages(); - initExcludeClassRegexs(); - } - - private static String getProperty(String key) { - if (null != props && AgentUtils.isNotBlank(key)) { - return props.getProperty(key); - } - return null; - } - - private static Properties getProperties(String propertiesFileName) { - Properties properties = new Properties(); - InputStream input = null; - try { - input = ConfigUtils.class.getClassLoader().getResourceAsStream("props/agent.properties"); - properties.load(input); - } catch (Exception e) { - System.err.println("未找到默认配置"); - } finally { - AgentUtils.closeQuietly(input); - } - if (AgentUtils.isNotBlank(propertiesFileName)) { - try { - input = new FileInputStream(new File(propertiesFileName)); - properties.load(input); - } catch (Exception e) { - System.err.println(e); - } finally { - AgentUtils.closeQuietly(input); - } - } - return properties; - } - - public static Set getExcludePackages() { - return excludePackages; - } - - private static void initExcluePackages() { - if (null == excludePackages) { - String excludeDefault = getProperty(ConfigConsts.EXCLUDE_PACKAGE_DEFAULT); - String excludes = getProperty(ConfigConsts.EXCLUDE_PACKAGE); - excludePackages = new HashSet(); - if (AgentUtils.isNotBlank(excludeDefault)) { - excludePackages.addAll(Arrays.asList(excludeDefault.split(";"))); - } - if (AgentUtils.isNotBlank(excludes)) { - excludePackages.addAll(Arrays.asList(excludes.split(";"))); - } - } - } - - public static Set getIncludePackages() { - return includePackages; - } - - private static void initIncludePackages() { - if (null == includePackages) { - String includes = getProperty(ConfigConsts.INCLUDE_PACKAGE); - includePackages = new HashSet(); - if (AgentUtils.isNotBlank(includes)) { - includePackages.addAll(Arrays.asList(includes.split(";"))); - } - } - } - - public static String getLogFileName() { - return getProperty(ConfigConsts.LOG_FILE); - } - - public static int getLogInterval() { - String intervalStr = getProperty(ConfigConsts.LOG_INTERVAL_SECONDS); - if (AgentUtils.isBlank(intervalStr)) { - return ConfigConsts.DEFAULT_LOG_INTERVAL; - } - return Integer.parseInt(intervalStr); - } - - public static Set getExcludeClassRegexs() { - return excludeClassRegexs; - } - - private static void initExcludeClassRegexs() { - if (null == excludeClassRegexs) { - Set excludeClassRegexsTemp = new HashSet(); - String defaultRegex = getProperty(ConfigConsts.EXCLUDE_CLASS_REGEX_DEFAULT); - if (AgentUtils.isNotBlank(defaultRegex)) { - excludeClassRegexsTemp.addAll(Arrays.asList(defaultRegex.split(";"))); - } - String excludeRegexStr = getProperty(ConfigConsts.EXCLUDE_CLASS_REGEX); - if (AgentUtils.isNotBlank(excludeRegexStr)) { - excludeClassRegexsTemp.addAll(Arrays.asList(excludeRegexStr.split(";"))); - } - excludeClassRegexs = excludeClassRegexsTemp; - } - } - - public static boolean isLogAvgExecuteTime() { - String value = getProperty(ConfigConsts.LOG_AVG_EXECUTE_TIME); - return "true".equalsIgnoreCase(value); - } - - /** - * 是否开启pojo的监控 - * - * @return 是否开启pojo的监控 - * @author dingjsh - */ - public static boolean isOpenPojoMonitor() { - String value = getProperty(ConfigConsts.POJO_MONITOR_OPEN); - return "true".equalsIgnoreCase(value); - } - - /** - * ConfigUtils - * - * @return 是否采用nanoTime来记录方法的执行时间 - * @description 是否采用nanoTime来记录方法的执行时间 - * @author dingjsh - * @date 2018年5月25日 下午2:08:33 - * @version 1.2.0 - */ - public static boolean isUsingNanoTime() { - String value = getProperty(ConfigConsts.LOG_TIME_NANO); - return "true".equalsIgnoreCase(value); - } -} diff --git a/src/com/wenshuo/agent/NamedThreadFactory.java b/src/com/wenshuo/agent/NamedThreadFactory.java deleted file mode 100644 index 6daceec..0000000 --- a/src/com/wenshuo/agent/NamedThreadFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.wenshuo.agent; - -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * 此类是拷贝的dubbo中的NamedThreadFactory,在创建线程池时总是没有名字,区分不出来是哪个线程池创建的,不便于调试 - * NamedThreadFactory - * @author dingjsh - * @time 2016-5-19上午09:40:50 - */ -public class NamedThreadFactory implements ThreadFactory { - private final AtomicInteger mThreadNum = new AtomicInteger(1); - - private final String mPrefix; - - private final boolean mDaemon; - - private final ThreadGroup mGroup; - - public NamedThreadFactory(String prefix, boolean daemon) { - mPrefix = prefix + "-thread-"; - mDaemon = daemon; - SecurityManager s = System.getSecurityManager(); - mGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s - .getThreadGroup(); - } - - public Thread newThread(Runnable runnable) { - String name = mPrefix + mThreadNum.getAndIncrement(); - Thread ret = new Thread(mGroup, runnable, name, 0); - ret.setDaemon(mDaemon); - return ret; - } -} \ No newline at end of file diff --git a/src/com/wenshuo/agent/PojoDetector.java b/src/com/wenshuo/agent/PojoDetector.java deleted file mode 100644 index 48cf45c..0000000 --- a/src/com/wenshuo/agent/PojoDetector.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * @(#)PojoDetector.java 2016-5-7 下午04:18:14 - * javaagent - * Copyright 2016 wenshuo, Inc. All rights reserved. - * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ -package com.wenshuo.agent; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import com.wenshuo.agent.javassist.CtMethod; - -/** - * PojoDetector - * @author dingjsh - * @time 2016-5-7下午04:18:14 - */ -public class PojoDetector { - - public static Set getPojoMethodNames(CtMethod[] methods){ - Set pojoMethods = Collections.emptySet(); - if(null != methods && methods.length>0){ - pojoMethods = new HashSet(); - Map propName2MethodName = new HashMap(); - for(CtMethod method : methods){ - String methodName = method.getName(); - if(isSetMethod(methodName) || isGetMethod(methodName)){ - String propName = getPojoPropertyName(methodName); - if(propName2MethodName.containsKey(propName)){ - pojoMethods.add(methodName); - pojoMethods.add(propName2MethodName.get(propName)); - }else{ - propName2MethodName.put(propName, methodName); - } - } - } - } - return pojoMethods; - } - - - - private static boolean isGetMethod(String methodName){ - return methodName.startsWith("get") || methodName.startsWith("is"); - } - - private static boolean isSetMethod(String methodName){ - return methodName.startsWith("set"); - } - - private static String getPojoPropertyName(String methodName){ - if(methodName.startsWith("get") || methodName.startsWith("set")){ - return methodName.substring(3); - }else if(methodName.startsWith("is")){ - return methodName.substring(2); - } - return ""; - } - - -} diff --git a/src/com/wenshuo/agent/javassist/ByteArrayClassPath.java b/src/com/wenshuo/agent/javassist/ByteArrayClassPath.java deleted file mode 100644 index d4d9347..0000000 --- a/src/com/wenshuo/agent/javassist/ByteArrayClassPath.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.*; -import java.net.URL; -import java.net.MalformedURLException; - -/** - * A ByteArrayClassPath contains bytes that is served as - * a class file to a ClassPool. It is useful to convert - * a byte array to a CtClass object. - * - *

For example, if you want to convert a byte array b - * into a CtClass object representing the class with a name - * classname, then do as following: - * - *

- * ClassPool cp = ClassPool.getDefault();
- * cp.insertClassPath(new ByteArrayClassPath(classname, b));
- * CtClass cc = cp.get(classname);
- * 
- * - *

The ClassPool object cp uses the created - * ByteArrayClassPath object as the source of the class file. - * - *

A ByteArrayClassPath must be instantiated for every - * class. It contains only a single class file. - * - * @see javassist.ClassPath - * @see ClassPool#insertClassPath(ClassPath) - * @see ClassPool#appendClassPath(ClassPath) - * @see ClassPool#makeClass(InputStream) - */ -public class ByteArrayClassPath implements ClassPath { - protected String classname; - protected byte[] classfile; - - /* - * Creates a ByteArrayClassPath containing the given - * bytes. - * - * @param name a fully qualified class name - * @param classfile the contents of a class file. - */ - public ByteArrayClassPath(String name, byte[] classfile) { - this.classname = name; - this.classfile = classfile; - } - - /** - * Closes this class path. - */ - public void close() {} - - public String toString() { - return "byte[]:" + classname; - } - - /** - * Opens the class file. - */ - public InputStream openClassfile(String classname) { - if(this.classname.equals(classname)) - return new ByteArrayInputStream(classfile); - else - return null; - } - - /** - * Obtains the URL. - */ - public URL find(String classname) { - if(this.classname.equals(classname)) { - String cname = classname.replace('.', '/') + ".class"; - try { - // return new File(cname).toURL(); - return new URL("file:/ByteArrayClassPath/" + cname); - } - catch (MalformedURLException e) {} - } - - return null; - } -} diff --git a/src/com/wenshuo/agent/javassist/CannotCompileException.java b/src/com/wenshuo/agent/javassist/CannotCompileException.java deleted file mode 100644 index 31057c8..0000000 --- a/src/com/wenshuo/agent/javassist/CannotCompileException.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Thrown when bytecode transformation has failed. - */ -public class CannotCompileException extends Exception { - private Throwable myCause; - - /** - * Gets the cause of this throwable. - * It is for JDK 1.3 compatibility. - */ - public Throwable getCause() { - return (myCause == this ? null : myCause); - } - - /** - * Initializes the cause of this throwable. - * It is for JDK 1.3 compatibility. - */ - public synchronized Throwable initCause(Throwable cause) { - myCause = cause; - return this; - } - - private String message; - - /** - * Gets a long message if it is available. - */ - public String getReason() { - if (message != null) - return message; - else - return this.toString(); - } - - /** - * Constructs a CannotCompileException with a message. - * - * @param msg the message. - */ - public CannotCompileException(String msg) { - super(msg); - message = msg; - initCause(null); - } - - /** - * Constructs a CannotCompileException with an Exception - * representing the cause. - * - * @param e the cause. - */ - public CannotCompileException(Throwable e) { - super("by " + e.toString()); - message = null; - initCause(e); - } - - /** - * Constructs a CannotCompileException with a detailed message - * and an Exception representing the cause. - * - * @param msg the message. - * @param e the cause. - */ - public CannotCompileException(String msg, Throwable e) { - this(msg); - initCause(e); - } - - /** - * Constructs a CannotCompileException with a - * NotFoundException. - */ - public CannotCompileException(NotFoundException e) { - this("cannot find " + e.getMessage(), e); - } - - /** - * Constructs a CannotCompileException with an CompileError. - */ - public CannotCompileException(CompileError e) { - this("[source error] " + e.getMessage(), e); - } - - /** - * Constructs a CannotCompileException - * with a ClassNotFoundException. - */ - public CannotCompileException(ClassNotFoundException e, String name) { - this("cannot find " + name, e); - } - - /** - * Constructs a CannotCompileException with a ClassFormatError. - */ - public CannotCompileException(ClassFormatError e, String name) { - this("invalid class format: " + name, e); - } -} diff --git a/src/com/wenshuo/agent/javassist/ClassClassPath.java b/src/com/wenshuo/agent/javassist/ClassClassPath.java deleted file mode 100644 index be42a50..0000000 --- a/src/com/wenshuo/agent/javassist/ClassClassPath.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.InputStream; -import java.net.URL; - -/** - * A search-path for obtaining a class file - * by getResourceAsStream() in java.lang.Class. - * - *

Try adding a ClassClassPath when a program is running - * with a user-defined class loader and any class files are not found with - * the default ClassPool. For example, - * - *

- * ClassPool cp = ClassPool.getDefault();
- * cp.insertClassPath(new ClassClassPath(this.getClass()));
- * 
- * - * This code snippet permanently adds a ClassClassPath - * to the default ClassPool. Note that the default - * ClassPool is a singleton. The added - * ClassClassPath uses a class object representing - * the class including the code snippet above. - * - * @see ClassPool#insertClassPath(ClassPath) - * @see ClassPool#appendClassPath(ClassPath) - * @see LoaderClassPath - */ -public class ClassClassPath implements ClassPath { - private Class thisClass; - - /** Creates a search path. - * - * @param c the Class object used to obtain a class - * file. getResourceAsStream() is called on - * this object. - */ - public ClassClassPath(Class c) { - thisClass = c; - } - - ClassClassPath() { - /* The value of thisClass was this.getClass() in early versions: - * - * thisClass = this.getClass(); - * - * However, this made openClassfile() not search all the system - * class paths if javassist.jar is put in jre/lib/ext/ - * (with JDK1.4). - */ - this(java.lang.Object.class); - } - - /** - * Obtains a class file by getResourceAsStream(). - */ - public InputStream openClassfile(String classname) { - String jarname = "/" + classname.replace('.', '/') + ".class"; - return thisClass.getResourceAsStream(jarname); - } - - /** - * Obtains the URL of the specified class file. - * - * @return null if the class file could not be found. - */ - public URL find(String classname) { - String jarname = "/" + classname.replace('.', '/') + ".class"; - return thisClass.getResource(jarname); - } - - /** - * Does nothing. - */ - public void close() { - } - - public String toString() { - return thisClass.getName() + ".class"; - } -} diff --git a/src/com/wenshuo/agent/javassist/ClassMap.java b/src/com/wenshuo/agent/javassist/ClassMap.java deleted file mode 100644 index 2dbf987..0000000 --- a/src/com/wenshuo/agent/javassist/ClassMap.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.Descriptor; - -/** - * A hash table associating class names with different names. - * - *

This hashtable is used for replacing class names in a class - * definition or a method body. Define a subclass of this class - * if a more complex mapping algorithm is needed. For example, - * - *

class MyClassMap extends ClassMap {
- *   public Object get(Object jvmClassName) {
- *     String name = toJavaName((String)jvmClassName);
- *     if (name.startsWith("java."))
- *         return toJvmName("java2." + name.substring(5));
- *     else
- *         return super.get(jvmClassName);
- *   }
- * }
- * - *

This subclass maps java.lang.String to - * java2.lang.String. Note that get() - * receives and returns the internal representation of a class name. - * For example, the internal representation of java.lang.String - * is java/lang/String. - * - *

Note that this is a map from String to String. - * - * @see #get(Object) - * @see CtClass#replaceClassName(ClassMap) - * @see CtNewMethod#copy(CtMethod,String,CtClass,ClassMap) - */ -public class ClassMap extends java.util.HashMap { - private ClassMap parent; - - /** - * Constructs a hash table. - */ - public ClassMap() { parent = null; } - - ClassMap(ClassMap map) { parent = map; } - - /** - * Maps a class name to another name in this hashtable. - * The names are obtained with calling Class.getName(). - * This method translates the given class names into the - * internal form used in the JVM before putting it in - * the hashtable. - * - * @param oldname the original class name - * @param newname the substituted class name. - */ - public void put(CtClass oldname, CtClass newname) { - put(oldname.getName(), newname.getName()); - } - - /** - * Maps a class name to another name in this hashtable. - * If the hashtable contains another mapping from the same - * class name, the old mapping is replaced. - * This method translates the given class names into the - * internal form used in the JVM before putting it in - * the hashtable. - * - *

If oldname is identical to - * newname, then this method does not - * perform anything; it does not record the mapping from - * oldname to newname. See - * fix method. - * - * @param oldname the original class name. - * @param newname the substituted class name. - * @see #fix(String) - */ - public void put(String oldname, String newname) { - if (oldname == newname) - return; - - String oldname2 = toJvmName(oldname); - String s = (String)get(oldname2); - if (s == null || !s.equals(oldname2)) - super.put(oldname2, toJvmName(newname)); - } - - /** - * Is equivalent to put() except that - * the given mapping is not recorded into the hashtable - * if another mapping from oldname is - * already included. - * - * @param oldname the original class name. - * @param newname the substituted class name. - */ - public void putIfNone(String oldname, String newname) { - if (oldname == newname) - return; - - String oldname2 = toJvmName(oldname); - String s = (String)get(oldname2); - if (s == null) - super.put(oldname2, toJvmName(newname)); - } - - protected final void put0(Object oldname, Object newname) { - super.put(oldname, newname); - } - - /** - * Returns the class name to wihch the given jvmClassName - * is mapped. A subclass of this class should override this method. - * - *

This method receives and returns the internal representation of - * class name used in the JVM. - * - * @see #toJvmName(String) - * @see #toJavaName(String) - */ - public Object get(Object jvmClassName) { - Object found = super.get(jvmClassName); - if (found == null && parent != null) - return parent.get(jvmClassName); - else - return found; - } - - /** - * Prevents a mapping from the specified class name to another name. - */ - public void fix(CtClass clazz) { - fix(clazz.getName()); - } - - /** - * Prevents a mapping from the specified class name to another name. - */ - public void fix(String name) { - String name2 = toJvmName(name); - super.put(name2, name2); - } - - /** - * Converts a class name into the internal representation used in - * the JVM. - */ - public static String toJvmName(String classname) { - return Descriptor.toJvmName(classname); - } - - /** - * Converts a class name from the internal representation used in - * the JVM to the normal one used in Java. - */ - public static String toJavaName(String classname) { - return Descriptor.toJavaName(classname); - } -} diff --git a/src/com/wenshuo/agent/javassist/ClassPath.java b/src/com/wenshuo/agent/javassist/ClassPath.java deleted file mode 100644 index f0e90e2..0000000 --- a/src/com/wenshuo/agent/javassist/ClassPath.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.InputStream; -import java.net.URL; - -/** - * ClassPath is an interface implemented by objects - * representing a class search path. - * ClassPool uses those objects for reading class files. - * - *

The users can define a class implementing this interface so that - * a class file is obtained from a non-standard source. - * - * @see ClassPool#insertClassPath(ClassPath) - * @see ClassPool#appendClassPath(ClassPath) - * @see ClassPool#removeClassPath(ClassPath) - */ -public interface ClassPath { - /** - * Opens a class file. - * This method may be called just to examine whether the class file - * exists as well as to read the contents of the file. - * - *

This method can return null if the specified class file is not - * found. If null is returned, the next search path is examined. - * However, if an error happens, this method must throw an exception - * so that the search will be terminated. - * - *

This method should not modify the contents of the class file. - * - * @param classname a fully-qualified class name - * @return the input stream for reading a class file - * @see javassist.Translator - */ - InputStream openClassfile(String classname) throws NotFoundException; - - /** - * Returns the uniform resource locator (URL) of the class file - * with the specified name. - * - * @param classname a fully-qualified class name. - * @return null if the specified class file could not be found. - */ - URL find(String classname); - - /** - * This method is invoked when the ClassPath object is - * detached from the search path. It will be an empty method in most of - * classes. - */ - void close(); -} diff --git a/src/com/wenshuo/agent/javassist/ClassPool.java b/src/com/wenshuo/agent/javassist/ClassPool.java deleted file mode 100644 index 2114b1f..0000000 --- a/src/com/wenshuo/agent/javassist/ClassPool.java +++ /dev/null @@ -1,1233 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Method; -import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.security.ProtectionDomain; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.ArrayList; -import java.util.Enumeration; - -import com.wenshuo.agent.javassist.bytecode.ClassFile; -import com.wenshuo.agent.javassist.bytecode.Descriptor; - -/** - * A container of CtClass objects. - * A CtClass object must be obtained from this object. - * If get() is called on this object, - * it searches various sources represented by ClassPath - * to find a class file and then it creates a CtClass object - * representing that class file. The created object is returned to the - * caller. - * - *

Memory consumption memo: - * - *

ClassPool objects hold all the CtClasses - * that have been created so that the consistency among modified classes - * can be guaranteed. Thus if a large number of CtClasses - * are processed, the ClassPool will consume a huge amount - * of memory. To avoid this, a ClassPool object - * should be recreated, for example, every hundred classes processed. - * Note that getDefault() is a singleton factory. - * Otherwise, detach() in CtClass should be used - * to avoid huge memory consumption. - * - *

ClassPool hierarchy: - * - *

ClassPools can make a parent-child hierarchy as - * java.lang.ClassLoaders. If a ClassPool has - * a parent pool, get() first asks the parent pool to find - * a class file. Only if the parent could not find the class file, - * get() searches the ClassPaths of - * the child ClassPool. This search order is reversed if - * ClassPath.childFirstLookup is true. - * - * @see javassist.CtClass - * @see javassist.ClassPath - */ -public class ClassPool { - // used by toClass(). - private static java.lang.reflect.Method defineClass1, defineClass2; - private static java.lang.reflect.Method definePackage; - - static { - try { - AccessController.doPrivileged(new PrivilegedExceptionAction(){ - public Object run() throws Exception{ - Class cl = Class.forName("java.lang.ClassLoader"); - defineClass1 = cl.getDeclaredMethod("defineClass", - new Class[] { String.class, byte[].class, - int.class, int.class }); - - defineClass2 = cl.getDeclaredMethod("defineClass", - new Class[] { String.class, byte[].class, - int.class, int.class, ProtectionDomain.class }); - - definePackage = cl.getDeclaredMethod("definePackage", - new Class[] { String.class, String.class, String.class, - String.class, String.class, String.class, - String.class, java.net.URL.class }); - return null; - } - }); - } - catch (PrivilegedActionException pae) { - throw new RuntimeException("cannot initialize ClassPool", pae.getException()); - } - } - - /** - * Determines the search order. - * - *

If this field is true, get() first searches the - * class path associated to this ClassPool and then - * the class path associated with the parent ClassPool. - * Otherwise, the class path associated with the parent is searched - * first. - * - *

The default value is false. - */ - public boolean childFirstLookup = false; - - /** - * Turning the automatic pruning on/off. - * - *

If this field is true, CtClass objects are - * automatically pruned by default when toBytecode() etc. - * are called. The automatic pruning can be turned on/off individually - * for each CtClass object. - * - *

The initial value is false. - * - * @see CtClass#prune() - * @see CtClass#stopPruning(boolean) - * @see CtClass#detach() - */ - public static boolean doPruning = false; - - private int compressCount; - private static final int COMPRESS_THRESHOLD = 100; - - /* releaseUnmodifiedClassFile was introduced for avoiding a bug - of JBoss AOP. So the value should be true except for JBoss AOP. - */ - - /** - * If true, unmodified and not-recently-used class files are - * periodically released for saving memory. - * - *

The initial value is true. - */ - public static boolean releaseUnmodifiedClassFile = true; - - protected ClassPoolTail source; - protected ClassPool parent; - protected Hashtable classes; // should be synchronous - - /** - * Table of registered cflow variables. - */ - private Hashtable cflow = null; // should be synchronous. - - private static final int INIT_HASH_SIZE = 191; - - private ArrayList importedPackages; - - /** - * Creates a root class pool. No parent class pool is specified. - */ - public ClassPool() { - this(null); - } - - /** - * Creates a root class pool. If useDefaultPath is - * true, appendSystemPath() is called. Otherwise, - * this constructor is equivalent to the constructor taking no - * parameter. - * - * @param useDefaultPath true if the system search path is - * appended. - */ - public ClassPool(boolean useDefaultPath) { - this(null); - if (useDefaultPath) - appendSystemPath(); - } - - /** - * Creates a class pool. - * - * @param parent the parent of this class pool. If this is a root - * class pool, this parameter must be null. - * @see javassist.ClassPool#getDefault() - */ - public ClassPool(ClassPool parent) { - this.classes = new Hashtable(INIT_HASH_SIZE); - this.source = new ClassPoolTail(); - this.parent = parent; - if (parent == null) { - CtClass[] pt = CtClass.primitiveTypes; - for (int i = 0; i < pt.length; ++i) - classes.put(pt[i].getName(), pt[i]); - } - - this.cflow = null; - this.compressCount = 0; - clearImportedPackages(); - } - - /** - * Returns the default class pool. - * The returned object is always identical since this method is - * a singleton factory. - * - *

The default class pool searches the system search path, - * which usually includes the platform library, extension - * libraries, and the search path specified by the - * -classpath option or the CLASSPATH - * environment variable. - * - *

When this method is called for the first time, the default - * class pool is created with the following code snippet: - * - *

ClassPool cp = new ClassPool();
-     * cp.appendSystemPath();
-     * 
- * - *

If the default class pool cannot find any class files, - * try ClassClassPath and LoaderClassPath. - * - * @see ClassClassPath - * @see LoaderClassPath - */ - public static synchronized ClassPool getDefault() { - if (defaultPool == null) { - defaultPool = new ClassPool(null); - defaultPool.appendSystemPath(); - } - - return defaultPool; - } - - private static ClassPool defaultPool = null; - - /** - * Provide a hook so that subclasses can do their own - * caching of classes. - * - * @see #cacheCtClass(String,CtClass,boolean) - * @see #removeCached(String) - */ - protected CtClass getCached(String classname) { - return (CtClass)classes.get(classname); - } - - /** - * Provides a hook so that subclasses can do their own - * caching of classes. - * - * @see #getCached(String) - * @see #removeCached(String) - */ - protected void cacheCtClass(String classname, CtClass c, boolean dynamic) { - classes.put(classname, c); - } - - /** - * Provide a hook so that subclasses can do their own - * caching of classes. - * - * @see #getCached(String) - * @see #cacheCtClass(String,CtClass,boolean) - */ - protected CtClass removeCached(String classname) { - return (CtClass)classes.remove(classname); - } - - /** - * Returns the class search path. - */ - public String toString() { - return source.toString(); - } - - /** - * This method is periodically invoked so that memory - * footprint will be minimized. - */ - void compress() { - if (compressCount++ > COMPRESS_THRESHOLD) { - compressCount = 0; - Enumeration e = classes.elements(); - while (e.hasMoreElements()) - ((CtClass)e.nextElement()).compress(); - } - } - - /** - * Record a package name so that the Javassist compiler searches - * the package to resolve a class name. - * Don't record the java.lang package, which has - * been implicitly recorded by default. - * - *

Since version 3.14, packageName can be a - * fully-qualified class name. - * - *

Note that get() in ClassPool does - * not search the recorded package. Only the compiler searches it. - * - * @param packageName the package name. - * It must not include the last '.' (dot). - * For example, "java.util" is valid but "java.util." is wrong. - * @since 3.1 - */ - public void importPackage(String packageName) { - importedPackages.add(packageName); - } - - /** - * Clear all the package names recorded by importPackage(). - * The java.lang package is not removed. - * - * @see #importPackage(String) - * @since 3.1 - */ - public void clearImportedPackages() { - importedPackages = new ArrayList(); - importedPackages.add("java.lang"); - } - - /** - * Returns all the package names recorded by importPackage(). - * - * @see #importPackage(String) - * @since 3.1 - */ - public Iterator getImportedPackages() { - return importedPackages.iterator(); - } - - /** - * Records a class name that never exists. - * For example, a package name can be recorded by this method. - * This would improve execution performance - * since get() quickly throw an exception - * without searching the class path at all - * if the given name is an invalid name recorded by this method. - * Note that searching the class path takes relatively long time. - * - *

The current implementation of this method performs nothing. - * - * @param name an invalid class name (separeted by dots). - * @deprecated - */ - public void recordInvalidClassName(String name) { - // source.recordInvalidClassName(name); - } - - /** - * Records the $cflow variable for the field specified - * by cname and fname. - * - * @param name variable name - * @param cname class name - * @param fname field name - */ - void recordCflow(String name, String cname, String fname) { - if (cflow == null) - cflow = new Hashtable(); - - cflow.put(name, new Object[] { cname, fname }); - } - - /** - * Undocumented method. Do not use; internal-use only. - * - * @param name the name of $cflow variable - */ - public Object[] lookupCflow(String name) { - if (cflow == null) - cflow = new Hashtable(); - - return (Object[])cflow.get(name); - } - - /** - * Reads a class file and constructs a CtClass - * object with a new name. - * This method is useful if you want to generate a new class as a copy - * of another class (except the class name). For example, - * - *

-     * getAndRename("Point", "Pair")
-     * 
- * - * returns a CtClass object representing Pair - * class. The definition of Pair is the same as that of - * Point class except the class name since Pair - * is defined by reading Point.class. - * - * @param orgName the original (fully-qualified) class name - * @param newName the new class name - */ - public CtClass getAndRename(String orgName, String newName) - throws NotFoundException - { - CtClass clazz = get0(orgName, false); - if (clazz == null) - throw new NotFoundException(orgName); - - if (clazz instanceof CtClassType) - ((CtClassType)clazz).setClassPool(this); - - clazz.setName(newName); // indirectly calls - // classNameChanged() in this class - return clazz; - } - - /* - * This method is invoked by CtClassType.setName(). It removes a - * CtClass object from the hash table and inserts it with the new - * name. Don't delegate to the parent. - */ - synchronized void classNameChanged(String oldname, CtClass clazz) { - CtClass c = (CtClass)getCached(oldname); - if (c == clazz) // must check this equation. - removeCached(oldname); // see getAndRename(). - - String newName = clazz.getName(); - checkNotFrozen(newName); - cacheCtClass(newName, clazz, false); - } - - /** - * Reads a class file from the source and returns a reference - * to the CtClass - * object representing that class file. If that class file has been - * already read, this method returns a reference to the - * CtClass created when that class file was read at the - * first time. - * - *

If classname ends with "[]", then this method - * returns a CtClass object for that array type. - * - *

To obtain an inner class, use "$" instead of "." for separating - * the enclosing class name and the inner class name. - * - * @param classname a fully-qualified class name. - */ - public CtClass get(String classname) throws NotFoundException { - CtClass clazz; - if (classname == null) - clazz = null; - else - clazz = get0(classname, true); - - if (clazz == null) - throw new NotFoundException(classname); - else { - clazz.incGetCounter(); - return clazz; - } - } - - /** - * Reads a class file from the source and returns a reference - * to the CtClass - * object representing that class file. - * This method is equivalent to get except - * that it returns null when a class file is - * not found and it never throws an exception. - * - * @param classname a fully-qualified class name. - * @return a CtClass object or null. - * @see #get(String) - * @see #find(String) - * @since 3.13 - */ - public CtClass getOrNull(String classname) { - CtClass clazz = null; - if (classname == null) - clazz = null; - else - try { - /* ClassPool.get0() never throws an exception - but its subclass may implement get0 that - may throw an exception. - */ - clazz = get0(classname, true); - } - catch (NotFoundException e){} - - if (clazz != null) - clazz.incGetCounter(); - - return clazz; - } - - /** - * Returns a CtClass object with the given name. - * This is almost equivalent to get(String) except - * that classname can be an array-type "descriptor" (an encoded - * type name) such as [Ljava/lang/Object;. - * - *

Using this method is not recommended; this method should be - * used only to obtain the CtClass object - * with a name returned from getClassInfo in - * javassist.bytecode.ClassPool. getClassInfo - * returns a fully-qualified class name but, if the class is an array - * type, it returns a descriptor. - * - * @param classname a fully-qualified class name or a descriptor - * representing an array type. - * @see #get(String) - * @see javassist.bytecode.ConstPool#getClassInfo(int) - * @see javassist.bytecode.Descriptor#toCtClass(String, ClassPool) - * @since 3.8.1 - */ - public CtClass getCtClass(String classname) throws NotFoundException { - if (classname.charAt(0) == '[') - return Descriptor.toCtClass(classname, this); - else - return get(classname); - } - - /** - * @param useCache false if the cached CtClass must be ignored. - * @return null if the class could not be found. - */ - protected synchronized CtClass get0(String classname, boolean useCache) - throws NotFoundException - { - CtClass clazz = null; - if (useCache) { - clazz = getCached(classname); - if (clazz != null) - return clazz; - } - - if (!childFirstLookup && parent != null) { - clazz = parent.get0(classname, useCache); - if (clazz != null) - return clazz; - } - - clazz = createCtClass(classname, useCache); - if (clazz != null) { - // clazz.getName() != classname if classname is "[L;". - if (useCache) - cacheCtClass(clazz.getName(), clazz, false); - - return clazz; - } - - if (childFirstLookup && parent != null) - clazz = parent.get0(classname, useCache); - - return clazz; - } - - /** - * Creates a CtClass object representing the specified class. - * It first examines whether or not the corresponding class - * file exists. If yes, it creates a CtClass object. - * - * @return null if the class file could not be found. - */ - protected CtClass createCtClass(String classname, boolean useCache) { - // accept "[L;" as a class name. - if (classname.charAt(0) == '[') - classname = Descriptor.toClassName(classname); - - if (classname.endsWith("[]")) { - String base = classname.substring(0, classname.indexOf('[')); - if ((!useCache || getCached(base) == null) && find(base) == null) - return null; - else - return new CtArray(classname, this); - } - else - if (find(classname) == null) - return null; - else - return new CtClassType(classname, this); - } - - /** - * Searches the class path to obtain the URL of the class file - * specified by classname. It is also used to determine whether - * the class file exists. - * - * @param classname a fully-qualified class name. - * @return null if the class file could not be found. - * @see CtClass#getURL() - */ - public URL find(String classname) { - return source.find(classname); - } - - /* - * Is invoked by CtClassType.setName() and methods in this class. - * This method throws an exception if the class is already frozen or - * if this class pool cannot edit the class since it is in a parent - * class pool. - * - * @see checkNotExists(String) - */ - void checkNotFrozen(String classname) throws RuntimeException { - CtClass clazz = getCached(classname); - if (clazz == null) { - if (!childFirstLookup && parent != null) { - try { - clazz = parent.get0(classname, true); - } - catch (NotFoundException e) {} - if (clazz != null) - throw new RuntimeException(classname - + " is in a parent ClassPool. Use the parent."); - } - } - else - if (clazz.isFrozen()) - throw new RuntimeException(classname - + ": frozen class (cannot edit)"); - } - - /* - * This method returns null if this or its parent class pool does - * not contain a CtClass object with the class name. - * - * @see checkNotFrozen(String) - */ - CtClass checkNotExists(String classname) { - CtClass clazz = getCached(classname); - if (clazz == null) - if (!childFirstLookup && parent != null) { - try { - clazz = parent.get0(classname, true); - } - catch (NotFoundException e) {} - } - - return clazz; - } - - /* for CtClassType.getClassFile2(). Don't delegate to the parent. - */ - InputStream openClassfile(String classname) throws NotFoundException { - return source.openClassfile(classname); - } - - void writeClassfile(String classname, OutputStream out) - throws NotFoundException, IOException, CannotCompileException - { - source.writeClassfile(classname, out); - } - - /** - * Reads class files from the source and returns an array of - * CtClass - * objects representing those class files. - * - *

If an element of classnames ends with "[]", - * then this method - * returns a CtClass object for that array type. - * - * @param classnames an array of fully-qualified class name. - */ - public CtClass[] get(String[] classnames) throws NotFoundException { - if (classnames == null) - return new CtClass[0]; - - int num = classnames.length; - CtClass[] result = new CtClass[num]; - for (int i = 0; i < num; ++i) - result[i] = get(classnames[i]); - - return result; - } - - /** - * Reads a class file and obtains a compile-time method. - * - * @param classname the class name - * @param methodname the method name - * @see CtClass#getDeclaredMethod(String) - */ - public CtMethod getMethod(String classname, String methodname) - throws NotFoundException - { - CtClass c = get(classname); - return c.getDeclaredMethod(methodname); - } - - /** - * Creates a new class (or interface) from the given class file. - * If there already exists a class with the same name, the new class - * overwrites that previous class. - * - *

This method is used for creating a CtClass object - * directly from a class file. The qualified class name is obtained - * from the class file; you do not have to explicitly give the name. - * - * @param classfile class file. - * @throws RuntimeException if there is a frozen class with the - * the same name. - * @see #makeClassIfNew(InputStream) - * @see javassist.ByteArrayClassPath - */ - public CtClass makeClass(InputStream classfile) - throws IOException, RuntimeException - { - return makeClass(classfile, true); - } - - /** - * Creates a new class (or interface) from the given class file. - * If there already exists a class with the same name, the new class - * overwrites that previous class. - * - *

This method is used for creating a CtClass object - * directly from a class file. The qualified class name is obtained - * from the class file; you do not have to explicitly give the name. - * - * @param classfile class file. - * @param ifNotFrozen throws a RuntimeException if this parameter is true - * and there is a frozen class with the same name. - * @see javassist.ByteArrayClassPath - */ - public CtClass makeClass(InputStream classfile, boolean ifNotFrozen) - throws IOException, RuntimeException - { - compress(); - classfile = new BufferedInputStream(classfile); - CtClass clazz = new CtClassType(classfile, this); - clazz.checkModify(); - String classname = clazz.getName(); - if (ifNotFrozen) - checkNotFrozen(classname); - - cacheCtClass(classname, clazz, true); - return clazz; - } - - /** - * Creates a new class (or interface) from the given class file. - * If there already exists a class with the same name, the new class - * overwrites that previous class. - * - *

This method is used for creating a CtClass object - * directly from a class file. The qualified class name is obtained - * from the class file; you do not have to explicitly give the name. - * - * @param classfile class file. - * @throws RuntimeException if there is a frozen class with the - * the same name. - * @since 3.20 - */ - public CtClass makeClass(ClassFile classfile) - throws RuntimeException - { - return makeClass(classfile, true); - } - - /** - * Creates a new class (or interface) from the given class file. - * If there already exists a class with the same name, the new class - * overwrites that previous class. - * - *

This method is used for creating a CtClass object - * directly from a class file. The qualified class name is obtained - * from the class file; you do not have to explicitly give the name. - * - * @param classfile class file. - * @param ifNotFrozen throws a RuntimeException if this parameter is true - * and there is a frozen class with the same name. - * @since 3.20 - */ - public CtClass makeClass(ClassFile classfile, boolean ifNotFrozen) - throws RuntimeException - { - compress(); - CtClass clazz = new CtClassType(classfile, this); - clazz.checkModify(); - String classname = clazz.getName(); - if (ifNotFrozen) - checkNotFrozen(classname); - - cacheCtClass(classname, clazz, true); - return clazz; - } - - /** - * Creates a new class (or interface) from the given class file. - * If there already exists a class with the same name, this method - * returns the existing class; a new class is never created from - * the given class file. - * - *

This method is used for creating a CtClass object - * directly from a class file. The qualified class name is obtained - * from the class file; you do not have to explicitly give the name. - * - * @param classfile the class file. - * @see #makeClass(InputStream) - * @see javassist.ByteArrayClassPath - * @since 3.9 - */ - public CtClass makeClassIfNew(InputStream classfile) - throws IOException, RuntimeException - { - compress(); - classfile = new BufferedInputStream(classfile); - CtClass clazz = new CtClassType(classfile, this); - clazz.checkModify(); - String classname = clazz.getName(); - CtClass found = checkNotExists(classname); - if (found != null) - return found; - else { - cacheCtClass(classname, clazz, true); - return clazz; - } - } - - /** - * Creates a new public class. - * If there already exists a class with the same name, the new class - * overwrites that previous class. - * - *

If no constructor is explicitly added to the created new - * class, Javassist generates constructors and adds it when - * the class file is generated. It generates a new constructor - * for each constructor of the super class. The new constructor - * takes the same set of parameters and invokes the - * corresponding constructor of the super class. All the received - * parameters are passed to it. - * - * @param classname a fully-qualified class name. - * @throws RuntimeException if the existing class is frozen. - */ - public CtClass makeClass(String classname) throws RuntimeException { - return makeClass(classname, null); - } - - /** - * Creates a new public class. - * If there already exists a class/interface with the same name, - * the new class overwrites that previous class. - * - *

If no constructor is explicitly added to the created new - * class, Javassist generates constructors and adds it when - * the class file is generated. It generates a new constructor - * for each constructor of the super class. The new constructor - * takes the same set of parameters and invokes the - * corresponding constructor of the super class. All the received - * parameters are passed to it. - * - * @param classname a fully-qualified class name. - * @param superclass the super class. - * @throws RuntimeException if the existing class is frozen. - */ - public synchronized CtClass makeClass(String classname, CtClass superclass) - throws RuntimeException - { - checkNotFrozen(classname); - CtClass clazz = new CtNewClass(classname, this, false, superclass); - cacheCtClass(classname, clazz, true); - return clazz; - } - - /** - * Creates a new public nested class. - * This method is called by {@link CtClassType#makeNestedClass()}. - * - * @param classname a fully-qualified class name. - * @return the nested class. - */ - synchronized CtClass makeNestedClass(String classname) { - checkNotFrozen(classname); - CtClass clazz = new CtNewNestedClass(classname, this, false, null); - cacheCtClass(classname, clazz, true); - return clazz; - } - - /** - * Creates a new public interface. - * If there already exists a class/interface with the same name, - * the new interface overwrites that previous one. - * - * @param name a fully-qualified interface name. - * @throws RuntimeException if the existing interface is frozen. - */ - public CtClass makeInterface(String name) throws RuntimeException { - return makeInterface(name, null); - } - - /** - * Creates a new public interface. - * If there already exists a class/interface with the same name, - * the new interface overwrites that previous one. - * - * @param name a fully-qualified interface name. - * @param superclass the super interface. - * @throws RuntimeException if the existing interface is frozen. - */ - public synchronized CtClass makeInterface(String name, CtClass superclass) - throws RuntimeException - { - checkNotFrozen(name); - CtClass clazz = new CtNewClass(name, this, true, superclass); - cacheCtClass(name, clazz, true); - return clazz; - } - - /** - * Creates a new annotation. - * If there already exists a class/interface with the same name, - * the new interface overwrites that previous one. - * - * @param name a fully-qualified interface name. - * Or null if the annotation has no super interface. - * @throws RuntimeException if the existing interface is frozen. - * @since 3.19 - */ - public CtClass makeAnnotation(String name) throws RuntimeException { - try { - CtClass cc = makeInterface(name, get("java.lang.annotation.Annotation")); - cc.setModifiers(cc.getModifiers() | Modifier.ANNOTATION); - return cc; - } - catch (NotFoundException e) { - // should never happen. - throw new RuntimeException(e.getMessage(), e); - } - } - - /** - * Appends the system search path to the end of the - * search path. The system search path - * usually includes the platform library, extension - * libraries, and the search path specified by the - * -classpath option or the CLASSPATH - * environment variable. - * - * @return the appended class path. - */ - public ClassPath appendSystemPath() { - return source.appendSystemPath(); - } - - /** - * Insert a ClassPath object at the head of the - * search path. - * - * @return the inserted class path. - * @see javassist.ClassPath - * @see javassist.URLClassPath - * @see javassist.ByteArrayClassPath - */ - public ClassPath insertClassPath(ClassPath cp) { - return source.insertClassPath(cp); - } - - /** - * Appends a ClassPath object to the end of the - * search path. - * - * @return the appended class path. - * @see javassist.ClassPath - * @see javassist.URLClassPath - * @see javassist.ByteArrayClassPath - */ - public ClassPath appendClassPath(ClassPath cp) { - return source.appendClassPath(cp); - } - - /** - * Inserts a directory or a jar (or zip) file at the head of the - * search path. - * - * @param pathname the path name of the directory or jar file. - * It must not end with a path separator ("/"). - * If the path name ends with "/*", then all the - * jar files matching the path name are inserted. - * - * @return the inserted class path. - * @throws NotFoundException if the jar file is not found. - */ - public ClassPath insertClassPath(String pathname) - throws NotFoundException - { - return source.insertClassPath(pathname); - } - - /** - * Appends a directory or a jar (or zip) file to the end of the - * search path. - * - * @param pathname the path name of the directory or jar file. - * It must not end with a path separator ("/"). - * If the path name ends with "/*", then all the - * jar files matching the path name are appended. - * - * @return the appended class path. - * @throws NotFoundException if the jar file is not found. - */ - public ClassPath appendClassPath(String pathname) - throws NotFoundException - { - return source.appendClassPath(pathname); - } - - /** - * Detatches the ClassPath object from the search path. - * The detached ClassPath object cannot be added - * to the path again. - */ - public void removeClassPath(ClassPath cp) { - source.removeClassPath(cp); - } - - /** - * Appends directories and jar files for search. - * - *

The elements of the given path list must be separated by colons - * in Unix or semi-colons in Windows. - * - * @param pathlist a (semi)colon-separated list of - * the path names of directories and jar files. - * The directory name must not end with a path - * separator ("/"). - * @throws NotFoundException if a jar file is not found. - */ - public void appendPathList(String pathlist) throws NotFoundException { - char sep = File.pathSeparatorChar; - int i = 0; - for (;;) { - int j = pathlist.indexOf(sep, i); - if (j < 0) { - appendClassPath(pathlist.substring(i)); - break; - } - else { - appendClassPath(pathlist.substring(i, j)); - i = j + 1; - } - } - } - - /** - * Converts the given class to a java.lang.Class object. - * Once this method is called, further modifications are not - * allowed any more. - * To load the class, this method uses the context class loader - * of the current thread. It is obtained by calling - * getClassLoader(). - * - *

This behavior can be changed by subclassing the pool and changing - * the getClassLoader() method. - * If the program is running on some application - * server, the context class loader might be inappropriate to load the - * class. - * - *

This method is provided for convenience. If you need more - * complex functionality, you should write your own class loader. - * - *

Warining: A Class object returned by this method may not - * work with a security manager or a signed jar file because a - * protection domain is not specified. - * - * @see #toClass(CtClass, java.lang.ClassLoader, ProtectionDomain) - * @see #getClassLoader() - */ - public Class toClass(CtClass clazz) throws CannotCompileException { - // Some subclasses of ClassPool may override toClass(CtClass,ClassLoader). - // So we should call that method instead of toClass(.., ProtectionDomain). - return toClass(clazz, getClassLoader()); - } - - /** - * Get the classloader for toClass(), getAnnotations() in - * CtClass, etc. - * - *

The default is the context class loader. - * - * @return the classloader for the pool - * @see #toClass(CtClass) - * @see CtClass#getAnnotations() - */ - public ClassLoader getClassLoader() { - return getContextClassLoader(); - } - - /** - * Obtains a class loader that seems appropriate to look up a class - * by name. - */ - static ClassLoader getContextClassLoader() { - return Thread.currentThread().getContextClassLoader(); - } - - /** - * Converts the class to a java.lang.Class object. - * Do not override this method any more at a subclass because - * toClass(CtClass) never calls this method. - * - *

Warining: A Class object returned by this method may not - * work with a security manager or a signed jar file because a - * protection domain is not specified. - * - * @deprecated Replaced by {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}. - * A subclass of ClassPool that has been - * overriding this method should be modified. It should override - * {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}. - */ - public Class toClass(CtClass ct, ClassLoader loader) - throws CannotCompileException - { - return toClass(ct, loader, null); - } - - /** - * Converts the class to a java.lang.Class object. - * Once this method is called, further modifications are not allowed - * any more. - * - *

The class file represented by the given CtClass is - * loaded by the given class loader to construct a - * java.lang.Class object. Since a private method - * on the class loader is invoked through the reflection API, - * the caller must have permissions to do that. - * - *

An easy way to obtain ProtectionDomain object is - * to call getProtectionDomain() - * in java.lang.Class. It returns the domain that the - * class belongs to. - * - *

This method is provided for convenience. If you need more - * complex functionality, you should write your own class loader. - * - * @param loader the class loader used to load this class. - * For example, the loader returned by - * getClassLoader() can be used - * for this parameter. - * @param domain the protection domain for the class. - * If it is null, the default domain created - * by java.lang.ClassLoader is used. - * - * @see #getClassLoader() - * @since 3.3 - */ - public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) - throws CannotCompileException - { - try { - byte[] b = ct.toBytecode(); - java.lang.reflect.Method method; - Object[] args; - if (domain == null) { - method = defineClass1; - args = new Object[] { ct.getName(), b, new Integer(0), - new Integer(b.length)}; - } - else { - method = defineClass2; - args = new Object[] { ct.getName(), b, new Integer(0), - new Integer(b.length), domain}; - } - - return (Class)toClass2(method, loader, args); - } - catch (RuntimeException e) { - throw e; - } - catch (java.lang.reflect.InvocationTargetException e) { - throw new CannotCompileException(e.getTargetException()); - } - catch (Exception e) { - throw new CannotCompileException(e); - } - } - - private static synchronized Object toClass2(Method method, - ClassLoader loader, Object[] args) - throws Exception - { - method.setAccessible(true); - try { - return method.invoke(loader, args); - } - finally { - method.setAccessible(false); - } - } - - /** - * Defines a new package. If the package is already defined, this method - * performs nothing. - * - *

You do not necessarily need to - * call this method. If this method is called, then - * getPackage() on the Class object returned - * by toClass() will return a non-null object. - * - * @param loader the class loader passed to toClass() or - * the default one obtained by getClassLoader(). - * @param name the package name. - * @see #getClassLoader() - * @see #toClass(CtClass) - * @see CtClass#toClass() - * @since 3.16 - */ - public void makePackage(ClassLoader loader, String name) - throws CannotCompileException - { - Object[] args = new Object[] { - name, null, null, null, null, null, null, null }; - Throwable t; - try { - toClass2(definePackage, loader, args); - return; - } - catch (java.lang.reflect.InvocationTargetException e) { - t = e.getTargetException(); - if (t == null) - t = e; - else if (t instanceof IllegalArgumentException) { - // if the package is already defined, an IllegalArgumentException - // is thrown. - return; - } - } - catch (Exception e) { - t = e; - } - - throw new CannotCompileException(t); - } -} diff --git a/src/com/wenshuo/agent/javassist/ClassPoolTail.java b/src/com/wenshuo/agent/javassist/ClassPoolTail.java deleted file mode 100644 index 638d201..0000000 --- a/src/com/wenshuo/agent/javassist/ClassPoolTail.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.*; -import java.util.jar.*; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Hashtable; - -final class ClassPathList { - ClassPathList next; - ClassPath path; - - ClassPathList(ClassPath p, ClassPathList n) { - next = n; - path = p; - } -} - -final class DirClassPath implements ClassPath { - String directory; - - DirClassPath(String dirName) { - directory = dirName; - } - - public InputStream openClassfile(String classname) { - try { - char sep = File.separatorChar; - String filename = directory + sep - + classname.replace('.', sep) + ".class"; - return new FileInputStream(filename.toString()); - } - catch (FileNotFoundException e) {} - catch (SecurityException e) {} - return null; - } - - public URL find(String classname) { - char sep = File.separatorChar; - String filename = directory + sep - + classname.replace('.', sep) + ".class"; - File f = new File(filename); - if (f.exists()) - try { - return f.getCanonicalFile().toURI().toURL(); - } - catch (MalformedURLException e) {} - catch (IOException e) {} - - return null; - } - - public void close() {} - - public String toString() { - return directory; - } -} - -final class JarDirClassPath implements ClassPath { - JarClassPath[] jars; - - JarDirClassPath(String dirName) throws NotFoundException { - File[] files = new File(dirName).listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - name = name.toLowerCase(); - return name.endsWith(".jar") || name.endsWith(".zip"); - } - }); - - if (files != null) { - jars = new JarClassPath[files.length]; - for (int i = 0; i < files.length; i++) - jars[i] = new JarClassPath(files[i].getPath()); - } - } - - public InputStream openClassfile(String classname) throws NotFoundException { - if (jars != null) - for (int i = 0; i < jars.length; i++) { - InputStream is = jars[i].openClassfile(classname); - if (is != null) - return is; - } - - return null; // not found - } - - public URL find(String classname) { - if (jars != null) - for (int i = 0; i < jars.length; i++) { - URL url = jars[i].find(classname); - if (url != null) - return url; - } - - return null; // not found - } - - public void close() { - if (jars != null) - for (int i = 0; i < jars.length; i++) - jars[i].close(); - } -} - -final class JarClassPath implements ClassPath { - JarFile jarfile; - String jarfileURL; - - JarClassPath(String pathname) throws NotFoundException { - try { - jarfile = new JarFile(pathname); - jarfileURL = new File(pathname).getCanonicalFile() - .toURI().toURL().toString(); - return; - } - catch (IOException e) {} - throw new NotFoundException(pathname); - } - - public InputStream openClassfile(String classname) - throws NotFoundException - { - try { - String jarname = classname.replace('.', '/') + ".class"; - JarEntry je = jarfile.getJarEntry(jarname); - if (je != null) - return jarfile.getInputStream(je); - else - return null; // not found - } - catch (IOException e) {} - throw new NotFoundException("broken jar file?: " - + jarfile.getName()); - } - - public URL find(String classname) { - String jarname = classname.replace('.', '/') + ".class"; - JarEntry je = jarfile.getJarEntry(jarname); - if (je != null) - try { - return new URL("jar:" + jarfileURL + "!/" + jarname); - } - catch (MalformedURLException e) {} - - return null; // not found - } - - public void close() { - try { - jarfile.close(); - jarfile = null; - } - catch (IOException e) {} - } - - public String toString() { - return jarfile == null ? "" : jarfile.toString(); - } -} - -final class ClassPoolTail { - protected ClassPathList pathList; - - public ClassPoolTail() { - pathList = null; - } - - public String toString() { - StringBuffer buf = new StringBuffer(); - buf.append("[class path: "); - ClassPathList list = pathList; - while (list != null) { - buf.append(list.path.toString()); - buf.append(File.pathSeparatorChar); - list = list.next; - } - - buf.append(']'); - return buf.toString(); - } - - public synchronized ClassPath insertClassPath(ClassPath cp) { - pathList = new ClassPathList(cp, pathList); - return cp; - } - - public synchronized ClassPath appendClassPath(ClassPath cp) { - ClassPathList tail = new ClassPathList(cp, null); - ClassPathList list = pathList; - if (list == null) - pathList = tail; - else { - while (list.next != null) - list = list.next; - - list.next = tail; - } - - return cp; - } - - public synchronized void removeClassPath(ClassPath cp) { - ClassPathList list = pathList; - if (list != null) - if (list.path == cp) - pathList = list.next; - else { - while (list.next != null) - if (list.next.path == cp) - list.next = list.next.next; - else - list = list.next; - } - - cp.close(); - } - - public ClassPath appendSystemPath() { - return appendClassPath(new ClassClassPath()); - } - - public ClassPath insertClassPath(String pathname) - throws NotFoundException - { - return insertClassPath(makePathObject(pathname)); - } - - public ClassPath appendClassPath(String pathname) - throws NotFoundException - { - return appendClassPath(makePathObject(pathname)); - } - - private static ClassPath makePathObject(String pathname) - throws NotFoundException - { - String lower = pathname.toLowerCase(); - if (lower.endsWith(".jar") || lower.endsWith(".zip")) - return new JarClassPath(pathname); - - int len = pathname.length(); - if (len > 2 && pathname.charAt(len - 1) == '*' - && (pathname.charAt(len - 2) == '/' - || pathname.charAt(len - 2) == File.separatorChar)) { - String dir = pathname.substring(0, len - 2); - return new JarDirClassPath(dir); - } - - return new DirClassPath(pathname); - } - - /** - * This method does not close the output stream. - */ - void writeClassfile(String classname, OutputStream out) - throws NotFoundException, IOException, CannotCompileException - { - InputStream fin = openClassfile(classname); - if (fin == null) - throw new NotFoundException(classname); - - try { - copyStream(fin, out); - } - finally { - fin.close(); - } - } - - /* - -- faster version -- - void checkClassName(String classname) throws NotFoundException { - if (find(classname) == null) - throw new NotFoundException(classname); - } - - -- slower version -- - - void checkClassName(String classname) throws NotFoundException { - InputStream fin = openClassfile(classname); - try { - fin.close(); - } - catch (IOException e) {} - } - */ - - - /** - * Opens the class file for the class specified by - * classname. - * - * @param classname a fully-qualified class name - * @return null if the file has not been found. - * @throws NotFoundException if any error is reported by ClassPath. - */ - InputStream openClassfile(String classname) - throws NotFoundException - { - ClassPathList list = pathList; - InputStream ins = null; - NotFoundException error = null; - while (list != null) { - try { - ins = list.path.openClassfile(classname); - } - catch (NotFoundException e) { - if (error == null) - error = e; - } - - if (ins == null) - list = list.next; - else - return ins; - } - - if (error != null) - throw error; - else - return null; // not found - } - - /** - * Searches the class path to obtain the URL of the class file - * specified by classname. It is also used to determine whether - * the class file exists. - * - * @param classname a fully-qualified class name. - * @return null if the class file could not be found. - */ - public URL find(String classname) { - ClassPathList list = pathList; - URL url = null; - while (list != null) { - url = list.path.find(classname); - if (url == null) - list = list.next; - else - return url; - } - - return null; - } - - /** - * Reads from an input stream until it reaches the end. - * - * @return the contents of that input stream - */ - public static byte[] readStream(InputStream fin) throws IOException { - byte[][] bufs = new byte[8][]; - int bufsize = 4096; - - for (int i = 0; i < 8; ++i) { - bufs[i] = new byte[bufsize]; - int size = 0; - int len = 0; - do { - len = fin.read(bufs[i], size, bufsize - size); - if (len >= 0) - size += len; - else { - byte[] result = new byte[bufsize - 4096 + size]; - int s = 0; - for (int j = 0; j < i; ++j) { - System.arraycopy(bufs[j], 0, result, s, s + 4096); - s = s + s + 4096; - } - - System.arraycopy(bufs[i], 0, result, s, size); - return result; - } - } while (size < bufsize); - bufsize *= 2; - } - - throw new IOException("too much data"); - } - - /** - * Reads from an input stream and write to an output stream - * until it reaches the end. This method does not close the - * streams. - */ - public static void copyStream(InputStream fin, OutputStream fout) - throws IOException - { - int bufsize = 4096; - byte[] buf = null; - for (int i = 0; i < 64; ++i) { - if (i < 8) { - bufsize *= 2; - buf = new byte[bufsize]; - } - int size = 0; - int len = 0; - do { - len = fin.read(buf, size, bufsize - size); - if (len >= 0) - size += len; - else { - fout.write(buf, 0, size); - return; - } - } while (size < bufsize); - fout.write(buf); - } - - throw new IOException("too much data"); - } -} diff --git a/src/com/wenshuo/agent/javassist/CodeConverter.java b/src/com/wenshuo/agent/javassist/CodeConverter.java deleted file mode 100644 index 5bd3014..0000000 --- a/src/com/wenshuo/agent/javassist/CodeConverter.java +++ /dev/null @@ -1,809 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.convert.*; - -/** - * Simple translator of method bodies - * (also see the javassist.expr package). - * - *

Instances of this class specifies how to instrument of the - * bytecodes representing a method body. They are passed to - * CtClass.instrument() or - * CtMethod.instrument() as a parameter. - * - *

Example: - *

- * ClassPool cp = ClassPool.getDefault();
- * CtClass point = cp.get("Point");
- * CtClass singleton = cp.get("Singleton");
- * CtClass client = cp.get("Client");
- * CodeConverter conv = new CodeConverter();
- * conv.replaceNew(point, singleton, "makePoint");
- * client.instrument(conv);
- * 
- * - *

This program substitutes "Singleton.makePoint()" - * for all occurrences of "new Point()" - * appearing in methods declared in a Client class. - * - * @see javassist.CtClass#instrument(CodeConverter) - * @see javassist.CtMethod#instrument(CodeConverter) - * @see javassist.expr.ExprEditor - */ -public class CodeConverter { - protected Transformer transformers = null; - - /** - * Modify a method body so that instantiation of the specified class - * is replaced with a call to the specified static method. For example, - * replaceNew(ctPoint, ctSingleton, "createPoint") - * (where ctPoint and ctSingleton are - * compile-time classes for class Point and class - * Singleton, respectively) - * replaces all occurrences of: - * - *

new Point(x, y)
- * - * in the method body with: - * - *
Singleton.createPoint(x, y)
- * - *

This enables to intercept instantiation of Point - * and change the samentics. For example, the following - * createPoint() implements the singleton pattern: - * - *

public static Point createPoint(int x, int y) {
-     *     if (aPoint == null)
-     *         aPoint = new Point(x, y);
-     *     return aPoint;
-     * }
-     * 
- * - *

The static method call substituted for the original new - * expression must be - * able to receive the same set of parameters as the original - * constructor. If there are multiple constructors with different - * parameter types, then there must be multiple static methods - * with the same name but different parameter types. - * - *

The return type of the substituted static method must be - * the exactly same as the type of the instantiated class specified by - * newClass. - * - * @param newClass the instantiated class. - * @param calledClass the class in which the static method is - * declared. - * @param calledMethod the name of the static method. - */ - public void replaceNew(CtClass newClass, - CtClass calledClass, String calledMethod) { - transformers = new TransformNew(transformers, newClass.getName(), - calledClass.getName(), calledMethod); - } - - /** - * Modify a method body so that instantiation of the class - * specified by oldClass - * is replaced with instantiation of another class newClass. - * For example, - * replaceNew(ctPoint, ctPoint2) - * (where ctPoint and ctPoint2 are - * compile-time classes for class Point and class - * Point2, respectively) - * replaces all occurrences of: - * - *

new Point(x, y)
- * - * in the method body with: - * - *
new Point2(x, y)
- * - *

Note that Point2 must be type-compatible with Point. - * It must have the same set of methods, fields, and constructors as the - * replaced class. - */ - public void replaceNew(CtClass oldClass, CtClass newClass) { - transformers = new TransformNewClass(transformers, oldClass.getName(), - newClass.getName()); - } - - /** - * Modify a method body so that field read/write expressions access - * a different field from the original one. - * - *

Note that this method changes only the filed name and the class - * declaring the field; the type of the target object does not change. - * Therefore, the substituted field must be declared in the same class - * or a superclass of the original class. - * - *

Also, clazz and newClass must specify - * the class directly declaring the field. They must not specify - * a subclass of that class. - * - * @param field the originally accessed field. - * @param newClass the class declaring the substituted field. - * @param newFieldname the name of the substituted field. - */ - public void redirectFieldAccess(CtField field, - CtClass newClass, String newFieldname) { - transformers = new TransformFieldAccess(transformers, field, - newClass.getName(), - newFieldname); - } - - /** - * Modify a method body so that an expression reading the specified - * field is replaced with a call to the specified static method. - * This static method receives the target object of the original - * read expression as a parameter. It must return a value of - * the same type as the field. - * - *

For example, the program below - * - *

Point p = new Point();
-     * int newX = p.x + 3;
- * - *

can be translated into: - * - *

Point p = new Point();
-     * int newX = Accessor.readX(p) + 3;
- * - *

where - * - *

public class Accessor {
-     *     public static int readX(Object target) { ... }
-     * }
- * - *

The type of the parameter of readX() must - * be java.lang.Object independently of the actual - * type of target. The return type must be the same - * as the field type. - * - * @param field the field. - * @param calledClass the class in which the static method is - * declared. - * @param calledMethod the name of the static method. - */ - public void replaceFieldRead(CtField field, - CtClass calledClass, String calledMethod) { - transformers = new TransformReadField(transformers, field, - calledClass.getName(), - calledMethod); - } - - /** - * Modify a method body so that an expression writing the specified - * field is replaced with a call to the specified static method. - * This static method receives two parameters: the target object of - * the original - * write expression and the assigned value. The return type of the - * static method is void. - * - *

For example, the program below - * - *

Point p = new Point();
-     * p.x = 3;
- * - *

can be translated into: - * - *

Point p = new Point();
-     * Accessor.writeX(3);
- * - *

where - * - *

public class Accessor {
-     *     public static void writeX(Object target, int value) { ... }
-     * }
- * - *

The type of the first parameter of writeX() must - * be java.lang.Object independently of the actual - * type of target. The type of the second parameter - * is the same as the field type. - * - * @param field the field. - * @param calledClass the class in which the static method is - * declared. - * @param calledMethod the name of the static method. - */ - public void replaceFieldWrite(CtField field, - CtClass calledClass, String calledMethod) { - transformers = new TransformWriteField(transformers, field, - calledClass.getName(), - calledMethod); - } - - /** - * Modify a method body, so that ALL accesses to an array are replaced with - * calls to static methods within another class. In the case of reading an - * element from the array, this is replaced with a call to a static method with - * the array and the index as arguments, the return value is the value read from - * the array. If writing to an array, this is replaced with a call to a static - * method with the array, index and new value as parameters, the return value of - * the static method is void. - * - *

The calledClass parameter is the class containing the static methods to be used - * for array replacement. The names parameter points to an implementation of - * ArrayAccessReplacementMethodNames which specifies the names of the method to be - * used for access for each type of array. For example reading from an int[] will - * require a different method than if writing to an int[], and writing to a long[] - * will require a different method than if writing to a byte[]. If the implementation - * of ArrayAccessReplacementMethodNames does not contain the name for access for a - * type of array, that access is not replaced. - * - *

A default implementation of ArrayAccessReplacementMethodNames called - * DefaultArrayAccessReplacementMethodNames has been provided and is what is used in the - * following example. This also assumes that 'foo.ArrayAdvisor' is the name of the - * CtClass passed in. - * - *

If we have the following class: - *

class POJO{
-     *    int[] ints = new int[]{1, 2, 3, 4, 5};
-     *    long[] longs = new int[]{10, 20, 30};
-     *    Object objects = new Object[]{true, false};
-     *    Integer[] integers = new Integer[]{new Integer(10)};
-     * }
-     * 
- * and this is accessed as: - *
POJO p = new POJO();
-     * 
-     * //Write to int array
-     * p.ints[2] = 7;
-     * 
-     * //Read from int array
-     * int i = p.ints[2];
-     * 
-     * //Write to long array
-     * p.longs[2] = 1000L;
-     * 
-     * //Read from long array
-     * long l = p.longs[2];
-     * 
-     * //Write to Object array
-     * p.objects[2] = "Hello";
-     * 
-     * //Read from Object array
-     * Object o = p.objects[2];
-     * 
-     * //Write to Integer array
-     * Integer integer = new Integer(5);
-     * p.integers[0] = integer;
-     * 
-     * //Read from Object array
-     * integer = p.integers[0];
-     * 
- * - * Following instrumentation we will have - *
POJO p = new POJO();
-     * 
-     * //Write to int array
-     * ArrayAdvisor.arrayWriteInt(p.ints, 2, 7);
-     * 
-     * //Read from int array
-     * int i = ArrayAdvisor.arrayReadInt(p.ints, 2);
-     * 
-     * //Write to long array
-     * ArrayAdvisor.arrayWriteLong(p.longs, 2, 1000L);
-     * 
-     * //Read from long array
-     * long l = ArrayAdvisor.arrayReadLong(p.longs, 2);
-     * 
-     * //Write to Object array
-     * ArrayAdvisor.arrayWriteObject(p.objects, 2, "Hello");
-     * 
-     * //Read from Object array
-     * Object o = ArrayAdvisor.arrayReadObject(p.objects, 2);
-     * 
-     * //Write to Integer array
-     * Integer integer = new Integer(5);
-     * ArrayAdvisor.arrayWriteObject(p.integers, 0, integer);
-     * 
-     * //Read from Object array
-     * integer = ArrayAdvisor.arrayWriteObject(p.integers, 0);
-     * 
- * - * @see DefaultArrayAccessReplacementMethodNames - * - * @param calledClass the class containing the static methods. - * @param names contains the names of the methods to replace - * the different kinds of array access with. - */ - public void replaceArrayAccess(CtClass calledClass, ArrayAccessReplacementMethodNames names) - throws NotFoundException - { - transformers = new TransformAccessArrayField(transformers, calledClass.getName(), names); - } - - /** - * Modify method invocations in a method body so that a different - * method will be invoked. - * - *

Note that the target object, the parameters, or - * the type of invocation - * (static method call, interface call, or private method call) - * are not modified. Only the method name is changed. The substituted - * method must have the same signature that the original one has. - * If the original method is a static method, the substituted method - * must be static. - * - * @param origMethod original method - * @param substMethod substituted method - */ - public void redirectMethodCall(CtMethod origMethod, - CtMethod substMethod) - throws CannotCompileException - { - String d1 = origMethod.getMethodInfo2().getDescriptor(); - String d2 = substMethod.getMethodInfo2().getDescriptor(); - if (!d1.equals(d2)) - throw new CannotCompileException("signature mismatch: " - + substMethod.getLongName()); - - int mod1 = origMethod.getModifiers(); - int mod2 = substMethod.getModifiers(); - if (Modifier.isStatic(mod1) != Modifier.isStatic(mod2) - || (Modifier.isPrivate(mod1) && !Modifier.isPrivate(mod2)) - || origMethod.getDeclaringClass().isInterface() - != substMethod.getDeclaringClass().isInterface()) - throw new CannotCompileException("invoke-type mismatch " - + substMethod.getLongName()); - - transformers = new TransformCall(transformers, origMethod, - substMethod); - } - - /** - * Correct invocations to a method that has been renamed. - * If a method is renamed, calls to that method must be also - * modified so that the method with the new name will be called. - * - *

The method must be declared in the same class before and - * after it is renamed. - * - *

Note that the target object, the parameters, or - * the type of invocation - * (static method call, interface call, or private method call) - * are not modified. Only the method name is changed. - * - * @param oldMethodName the old name of the method. - * @param newMethod the method with the new name. - * @see javassist.CtMethod#setName(String) - */ - public void redirectMethodCall(String oldMethodName, - CtMethod newMethod) - throws CannotCompileException - { - transformers - = new TransformCall(transformers, oldMethodName, newMethod); - } - - /** - * Insert a call to another method before an existing method call. - * That "before" method must be static. The return type must be - * void. As parameters, the before method receives - * the target object and all the parameters to the originally invoked - * method. For example, if the originally invoked method is - * move(): - * - *

class Point {
-     *     Point move(int x, int y) { ... }
-     * }
- * - *

Then the before method must be something like this: - * - *

class Verbose {
-     *     static void print(Point target, int x, int y) { ... }
-     * }
- * - *

The CodeConverter would translate bytecode - * equivalent to: - * - *

Point p2 = p.move(x + y, 0);
- * - *

into the bytecode equivalent to: - * - *

int tmp1 = x + y;
-     * int tmp2 = 0;
-     * Verbose.print(p, tmp1, tmp2);
-     * Point p2 = p.move(tmp1, tmp2);
- * - * @param origMethod the method originally invoked. - * @param beforeMethod the method invoked before - * origMethod. - */ - public void insertBeforeMethod(CtMethod origMethod, - CtMethod beforeMethod) - throws CannotCompileException - { - try { - transformers = new TransformBefore(transformers, origMethod, - beforeMethod); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - } - - /** - * Inserts a call to another method after an existing method call. - * That "after" method must be static. The return type must be - * void. As parameters, the after method receives - * the target object and all the parameters to the originally invoked - * method. For example, if the originally invoked method is - * move(): - * - *
class Point {
-     *     Point move(int x, int y) { ... }
-     * }
- * - *

Then the after method must be something like this: - * - *

class Verbose {
-     *     static void print(Point target, int x, int y) { ... }
-     * }
- * - *

The CodeConverter would translate bytecode - * equivalent to: - * - *

Point p2 = p.move(x + y, 0);
- * - *

into the bytecode equivalent to: - * - *

-     * int tmp1 = x + y;
-     * int tmp2 = 0;
-     * Point p2 = p.move(tmp1, tmp2);
-     * Verbose.print(p, tmp1, tmp2);
- * - * @param origMethod the method originally invoked. - * @param afterMethod the method invoked after - * origMethod. - */ - public void insertAfterMethod(CtMethod origMethod, - CtMethod afterMethod) - throws CannotCompileException - { - try { - transformers = new TransformAfter(transformers, origMethod, - afterMethod); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - } - - /** - * Performs code conversion. - */ - protected void doit(CtClass clazz, MethodInfo minfo, ConstPool cp) - throws CannotCompileException - { - Transformer t; - CodeAttribute codeAttr = minfo.getCodeAttribute(); - if (codeAttr == null || transformers == null) - return; - for (t = transformers; t != null; t = t.getNext()) - t.initialize(cp, clazz, minfo); - - CodeIterator iterator = codeAttr.iterator(); - while (iterator.hasNext()) { - try { - int pos = iterator.next(); - for (t = transformers; t != null; t = t.getNext()) - pos = t.transform(clazz, pos, iterator, cp); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - int locals = 0; - int stack = 0; - for (t = transformers; t != null; t = t.getNext()) { - int s = t.extraLocals(); - if (s > locals) - locals = s; - - s = t.extraStack(); - if (s > stack) - stack = s; - } - - for (t = transformers; t != null; t = t.getNext()) - t.clean(); - - if (locals > 0) - codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals); - - if (stack > 0) - codeAttr.setMaxStack(codeAttr.getMaxStack() + stack); - - try { - minfo.rebuildStackMapIf6(clazz.getClassPool(), - clazz.getClassFile2()); - } - catch (BadBytecode b) { - throw new CannotCompileException(b.getMessage(), b); - } - } - - /** - * Interface containing the method names to be used - * as array access replacements. - * - * @author
Kabir Khan - * @version $Revision: 1.16 $ - */ - public interface ArrayAccessReplacementMethodNames - { - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;I)B to replace reading from a byte[]. - */ - String byteOrBooleanRead(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;IB)V to replace writing to a byte[]. - */ - String byteOrBooleanWrite(); - - /** - * @return the name of a static method with the signature - * (Ljava/lang/Object;I)C to replace reading from a char[]. - */ - String charRead(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;IC)V to replace writing to a byte[]. - */ - String charWrite(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;I)D to replace reading from a double[]. - */ - String doubleRead(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;ID)V to replace writing to a double[]. - */ - String doubleWrite(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;I)F to replace reading from a float[]. - */ - String floatRead(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;IF)V to replace writing to a float[]. - */ - String floatWrite(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;I)I to replace reading from a int[]. - */ - String intRead(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;II)V to replace writing to a int[]. - */ - String intWrite(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;I)J to replace reading from a long[]. - */ - String longRead(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;IJ)V to replace writing to a long[]. - */ - String longWrite(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;I)Ljava/lang/Object; - * to replace reading from a Object[] (or any subclass of object). - */ - String objectRead(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;ILjava/lang/Object;)V - * to replace writing to a Object[] (or any subclass of object). - */ - String objectWrite(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;I)S to replace reading from a short[]. - */ - String shortRead(); - - /** - * Returns the name of a static method with the signature - * (Ljava/lang/Object;IS)V to replace writing to a short[]. - */ - String shortWrite(); - } - - /** - * Default implementation of the ArrayAccessReplacementMethodNames - * interface giving default values for method names to be used for replacing - * accesses to array elements. - * - * @author Kabir Khan - * @version $Revision: 1.16 $ - */ - public static class DefaultArrayAccessReplacementMethodNames - implements ArrayAccessReplacementMethodNames - { - /** - * Returns "arrayReadByteOrBoolean" as the name of the static method with the signature - * (Ljava/lang/Object;I)B to replace reading from a byte[]. - */ - public String byteOrBooleanRead() - { - return "arrayReadByteOrBoolean"; - } - - /** - * Returns "arrayWriteByteOrBoolean" as the name of the static method with the signature - * (Ljava/lang/Object;IB)V to replace writing to a byte[]. - */ - public String byteOrBooleanWrite() - { - return "arrayWriteByteOrBoolean"; - } - - /** - * Returns "arrayReadChar" as the name of the static method with the signature - * (Ljava/lang/Object;I)C to replace reading from a char[]. - */ - public String charRead() - { - return "arrayReadChar"; - } - - /** - * Returns "arrayWriteChar" as the name of the static method with the signature - * (Ljava/lang/Object;IC)V to replace writing to a byte[]. - */ - public String charWrite() - { - return "arrayWriteChar"; - } - - /** - * Returns "arrayReadDouble" as the name of the static method with the signature - * (Ljava/lang/Object;I)D to replace reading from a double[]. - */ - public String doubleRead() - { - return "arrayReadDouble"; - } - - /** - * Returns "arrayWriteDouble" as the name of the static method with the signature - * (Ljava/lang/Object;ID)V to replace writing to a double[]. - */ - public String doubleWrite() - { - return "arrayWriteDouble"; - } - - /** - * Returns "arrayReadFloat" as the name of the static method with the signature - * (Ljava/lang/Object;I)F to replace reading from a float[]. - */ - public String floatRead() - { - return "arrayReadFloat"; - } - - /** - * Returns "arrayWriteFloat" as the name of the static method with the signature - * (Ljava/lang/Object;IF)V to replace writing to a float[]. - */ - public String floatWrite() - { - return "arrayWriteFloat"; - } - - /** - * Returns "arrayReadInt" as the name of the static method with the signature - * (Ljava/lang/Object;I)I to replace reading from a int[]. - */ - public String intRead() - { - return "arrayReadInt"; - } - - /** - * Returns "arrayWriteInt" as the name of the static method with the signature - * (Ljava/lang/Object;II)V to replace writing to a int[]. - */ - public String intWrite() - { - return "arrayWriteInt"; - } - - /** - * Returns "arrayReadLong" as the name of the static method with the signature - * (Ljava/lang/Object;I)J to replace reading from a long[]. - */ - public String longRead() - { - return "arrayReadLong"; - } - - /** - * Returns "arrayWriteLong" as the name of the static method with the signature - * (Ljava/lang/Object;IJ)V to replace writing to a long[]. - */ - public String longWrite() - { - return "arrayWriteLong"; - } - - /** - * Returns "arrayReadObject" as the name of the static method with the signature - * (Ljava/lang/Object;I)Ljava/lang/Object; to replace reading from a Object[] (or any subclass of object). - */ - public String objectRead() - { - return "arrayReadObject"; - } - - /** - * Returns "arrayWriteObject" as the name of the static method with the signature - * (Ljava/lang/Object;ILjava/lang/Object;)V to replace writing to a Object[] (or any subclass of object). - */ - public String objectWrite() - { - return "arrayWriteObject"; - } - - /** - * Returns "arrayReadShort" as the name of the static method with the signature - * (Ljava/lang/Object;I)S to replace reading from a short[]. - */ - public String shortRead() - { - return "arrayReadShort"; - } - - /** - * Returns "arrayWriteShort" as the name of the static method with the signature - * (Ljava/lang/Object;IS)V to replace writing to a short[]. - */ - public String shortWrite() - { - return "arrayWriteShort"; - } - } -} diff --git a/src/com/wenshuo/agent/javassist/CtArray.java b/src/com/wenshuo/agent/javassist/CtArray.java deleted file mode 100644 index 3864c6a..0000000 --- a/src/com/wenshuo/agent/javassist/CtArray.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -/** - * Array types. - */ -final class CtArray extends CtClass { - protected ClassPool pool; - - // the name of array type ends with "[]". - CtArray(String name, ClassPool cp) { - super(name); - pool = cp; - } - - public ClassPool getClassPool() { - return pool; - } - - public boolean isArray() { - return true; - } - - private CtClass[] interfaces = null; - - public int getModifiers() { - int mod = Modifier.FINAL; - try { - mod |= getComponentType().getModifiers() - & (Modifier.PROTECTED | Modifier.PUBLIC | Modifier.PRIVATE); - } - catch (NotFoundException e) {} - return mod; - } - - public CtClass[] getInterfaces() throws NotFoundException { - if (interfaces == null) { - Class[] intfs = Object[].class.getInterfaces(); - // java.lang.Cloneable and java.io.Serializable. - // If the JVM is CLDC, intfs is empty. - interfaces = new CtClass[intfs.length]; - for (int i = 0; i < intfs.length; i++) - interfaces[i] = pool.get(intfs[i].getName()); - } - - return interfaces; - } - - public boolean subtypeOf(CtClass clazz) throws NotFoundException { - if (super.subtypeOf(clazz)) - return true; - - String cname = clazz.getName(); - if (cname.equals(javaLangObject)) - return true; - - CtClass[] intfs = getInterfaces(); - for (int i = 0; i < intfs.length; i++) - if (intfs[i].subtypeOf(clazz)) - return true; - - return clazz.isArray() - && getComponentType().subtypeOf(clazz.getComponentType()); - } - - public CtClass getComponentType() throws NotFoundException { - String name = getName(); - return pool.get(name.substring(0, name.length() - 2)); - } - - public CtClass getSuperclass() throws NotFoundException { - return pool.get(javaLangObject); - } - - public CtMethod[] getMethods() { - try { - return getSuperclass().getMethods(); - } - catch (NotFoundException e) { - return super.getMethods(); - } - } - - public CtMethod getMethod(String name, String desc) - throws NotFoundException - { - return getSuperclass().getMethod(name, desc); - } - - public CtConstructor[] getConstructors() { - try { - return getSuperclass().getConstructors(); - } - catch (NotFoundException e) { - return super.getConstructors(); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/CtBehavior.java b/src/com/wenshuo/agent/javassist/CtBehavior.java deleted file mode 100644 index ff03e03..0000000 --- a/src/com/wenshuo/agent/javassist/CtBehavior.java +++ /dev/null @@ -1,1219 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.Javac; -import com.wenshuo.agent.javassist.compiler.CompileError; -import com.wenshuo.agent.javassist.expr.ExprEditor; - -/** - * CtBehavior represents a method, a constructor, - * or a static constructor (class initializer). - * It is the abstract super class of - * CtMethod and CtConstructor. - * - *

To directly read or modify bytecode, obtain MethodInfo - * objects. - * - * @see #getMethodInfo() - */ -public abstract class CtBehavior extends CtMember { - protected MethodInfo methodInfo; - - protected CtBehavior(CtClass clazz, MethodInfo minfo) { - super(clazz); - methodInfo = minfo; - } - - /** - * @param isCons true if this is a constructor. - */ - void copy(CtBehavior src, boolean isCons, ClassMap map) - throws CannotCompileException - { - CtClass declaring = declaringClass; - MethodInfo srcInfo = src.methodInfo; - CtClass srcClass = src.getDeclaringClass(); - ConstPool cp = declaring.getClassFile2().getConstPool(); - - map = new ClassMap(map); - map.put(srcClass.getName(), declaring.getName()); - try { - boolean patch = false; - CtClass srcSuper = srcClass.getSuperclass(); - CtClass destSuper = declaring.getSuperclass(); - String destSuperName = null; - if (srcSuper != null && destSuper != null) { - String srcSuperName = srcSuper.getName(); - destSuperName = destSuper.getName(); - if (!srcSuperName.equals(destSuperName)) - if (srcSuperName.equals(CtClass.javaLangObject)) - patch = true; - else - map.putIfNone(srcSuperName, destSuperName); - } - - // a stack map table is copied from srcInfo. - methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map); - if (isCons && patch) - methodInfo.setSuperclass(destSuperName); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - protected void extendToString(StringBuffer buffer) { - buffer.append(' '); - buffer.append(getName()); - buffer.append(' '); - buffer.append(methodInfo.getDescriptor()); - } - - /** - * Returns the method or constructor name followed by parameter types - * such as javassist.CtBehavior.stBody(String). - * - * @since 3.5 - */ - public abstract String getLongName(); - - /** - * Returns the MethodInfo representing this method/constructor in the - * class file. - * - *

If you modify the bytecode through the returned - * MethodInfo object, you might have to explicitly - * rebuild a stack map table. Javassist does not automatically - * rebuild it for avoiding unnecessary rebuilding. - * - * @see javassist.bytecode.MethodInfo#rebuildStackMap(ClassPool) - */ - public MethodInfo getMethodInfo() { - declaringClass.checkModify(); - return methodInfo; - } - - /** - * Returns the MethodInfo representing the method/constructor in the - * class file (read only). - * Normal applications do not need calling this method. Use - * getMethodInfo(). - * - *

The MethodInfo object obtained by this method - * is read only. Changes to this object might not be reflected - * on a class file generated by toBytecode(), - * toClass(), etc in CtClass. - * - *

This method is available even if the CtClass - * containing this method is frozen. However, if the class is - * frozen, the MethodInfo might be also pruned. - * - * @see #getMethodInfo() - * @see CtClass#isFrozen() - * @see CtClass#prune() - */ - public MethodInfo getMethodInfo2() { return methodInfo; } - - /** - * Obtains the modifiers of the method/constructor. - * - * @return modifiers encoded with - * javassist.Modifier. - * @see Modifier - */ - public int getModifiers() { - return AccessFlag.toModifier(methodInfo.getAccessFlags()); - } - - /** - * Sets the encoded modifiers of the method/constructor. - * - *

Changing the modifiers may cause a problem. - * For example, if a non-static method is changed to static, - * the method will be rejected by the bytecode verifier. - * - * @see Modifier - */ - public void setModifiers(int mod) { - declaringClass.checkModify(); - methodInfo.setAccessFlags(AccessFlag.of(mod)); - } - - /** - * Returns true if the class has the specified annotation class. - * - * @param clz the annotation class. - * @return true if the annotation is found, - * otherwise false. - * @since 3.11 - */ - public boolean hasAnnotation(Class clz) { - MethodInfo mi = getMethodInfo2(); - AnnotationsAttribute ainfo = (AnnotationsAttribute) - mi.getAttribute(AnnotationsAttribute.invisibleTag); - AnnotationsAttribute ainfo2 = (AnnotationsAttribute) - mi.getAttribute(AnnotationsAttribute.visibleTag); - return CtClassType.hasAnnotationType(clz, - getDeclaringClass().getClassPool(), - ainfo, ainfo2); - } - - /** - * Returns the annotation if the class has the specified annotation class. - * For example, if an annotation @Author is associated - * with this method/constructor, an Author object is returned. - * The member values can be obtained by calling methods on - * the Author object. - * - * @param clz the annotation class. - * @return the annotation if found, otherwise null. - * @since 3.11 - */ - public Object getAnnotation(Class clz) throws ClassNotFoundException { - MethodInfo mi = getMethodInfo2(); - AnnotationsAttribute ainfo = (AnnotationsAttribute) - mi.getAttribute(AnnotationsAttribute.invisibleTag); - AnnotationsAttribute ainfo2 = (AnnotationsAttribute) - mi.getAttribute(AnnotationsAttribute.visibleTag); - return CtClassType.getAnnotationType(clz, - getDeclaringClass().getClassPool(), - ainfo, ainfo2); - } - - /** - * Returns the annotations associated with this method or constructor. - * - * @return an array of annotation-type objects. - * @see #getAvailableAnnotations() - * @since 3.1 - */ - public Object[] getAnnotations() throws ClassNotFoundException { - return getAnnotations(false); - } - - /** - * Returns the annotations associated with this method or constructor. - * If any annotations are not on the classpath, they are not included - * in the returned array. - * - * @return an array of annotation-type objects. - * @see #getAnnotations() - * @since 3.3 - */ - public Object[] getAvailableAnnotations(){ - try{ - return getAnnotations(true); - } - catch (ClassNotFoundException e){ - throw new RuntimeException("Unexpected exception", e); - } - } - - private Object[] getAnnotations(boolean ignoreNotFound) - throws ClassNotFoundException - { - MethodInfo mi = getMethodInfo2(); - AnnotationsAttribute ainfo = (AnnotationsAttribute) - mi.getAttribute(AnnotationsAttribute.invisibleTag); - AnnotationsAttribute ainfo2 = (AnnotationsAttribute) - mi.getAttribute(AnnotationsAttribute.visibleTag); - return CtClassType.toAnnotationType(ignoreNotFound, - getDeclaringClass().getClassPool(), - ainfo, ainfo2); - } - - /** - * Returns the parameter annotations associated with this method or constructor. - * - * @return an array of annotation-type objects. The length of the returned array is - * equal to the number of the formal parameters. If each parameter has no - * annotation, the elements of the returned array are empty arrays. - * - * @see #getAvailableParameterAnnotations() - * @see #getAnnotations() - * @since 3.1 - */ - public Object[][] getParameterAnnotations() throws ClassNotFoundException { - return getParameterAnnotations(false); - } - - /** - * Returns the parameter annotations associated with this method or constructor. - * If any annotations are not on the classpath, they are not included in the - * returned array. - * - * @return an array of annotation-type objects. The length of the returned array is - * equal to the number of the formal parameters. If each parameter has no - * annotation, the elements of the returned array are empty arrays. - * - * @see #getParameterAnnotations() - * @see #getAvailableAnnotations() - * @since 3.3 - */ - public Object[][] getAvailableParameterAnnotations(){ - try { - return getParameterAnnotations(true); - } - catch(ClassNotFoundException e) { - throw new RuntimeException("Unexpected exception", e); - } - } - - Object[][] getParameterAnnotations(boolean ignoreNotFound) - throws ClassNotFoundException - { - MethodInfo mi = getMethodInfo2(); - ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute) - mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag); - ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute) - mi.getAttribute(ParameterAnnotationsAttribute.visibleTag); - return CtClassType.toAnnotationType(ignoreNotFound, - getDeclaringClass().getClassPool(), - ainfo, ainfo2, mi); - } - - /** - * Obtains parameter types of this method/constructor. - */ - public CtClass[] getParameterTypes() throws NotFoundException { - return Descriptor.getParameterTypes(methodInfo.getDescriptor(), - declaringClass.getClassPool()); - } - - /** - * Obtains the type of the returned value. - */ - CtClass getReturnType0() throws NotFoundException { - return Descriptor.getReturnType(methodInfo.getDescriptor(), - declaringClass.getClassPool()); - } - - /** - * Returns the method signature (the parameter types - * and the return type). - * The method signature is represented by a character string - * called method descriptor, which is defined in the JVM specification. - * If two methods/constructors have - * the same parameter types - * and the return type, getSignature() returns the - * same string (the return type of constructors is void). - * - *

Note that the returned string is not the type signature - * contained in the SignatureAttirbute. It is - * a descriptor. - * - * @see javassist.bytecode.Descriptor - * @see #getGenericSignature() - */ - public String getSignature() { - return methodInfo.getDescriptor(); - } - - /** - * Returns the generic signature of the method. - * It represents parameter types including type variables. - * - * @see SignatureAttribute#toMethodSignature(String) - * @since 3.17 - */ - public String getGenericSignature() { - SignatureAttribute sa - = (SignatureAttribute)methodInfo.getAttribute(SignatureAttribute.tag); - return sa == null ? null : sa.getSignature(); - } - - /** - * Set the generic signature of the method. - * It represents parameter types including type variables. - * See {@link javassist.CtClass#setGenericSignature(String)} - * for a code sample. - * - * @param sig a new generic signature. - * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode() - * @since 3.17 - */ - public void setGenericSignature(String sig) { - declaringClass.checkModify(); - methodInfo.addAttribute(new SignatureAttribute(methodInfo.getConstPool(), sig)); - } - - /** - * Obtains exceptions that this method/constructor may throw. - * - * @return a zero-length array if there is no throws clause. - */ - public CtClass[] getExceptionTypes() throws NotFoundException { - String[] exceptions; - ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); - if (ea == null) - exceptions = null; - else - exceptions = ea.getExceptions(); - - return declaringClass.getClassPool().get(exceptions); - } - - /** - * Sets exceptions that this method/constructor may throw. - */ - public void setExceptionTypes(CtClass[] types) throws NotFoundException { - declaringClass.checkModify(); - if (types == null || types.length == 0) { - methodInfo.removeExceptionsAttribute(); - return; - } - - String[] names = new String[types.length]; - for (int i = 0; i < types.length; ++i) - names[i] = types[i].getName(); - - ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); - if (ea == null) { - ea = new ExceptionsAttribute(methodInfo.getConstPool()); - methodInfo.setExceptionsAttribute(ea); - } - - ea.setExceptions(names); - } - - /** - * Returns true if the body is empty. - */ - public abstract boolean isEmpty(); - - /** - * Sets a method/constructor body. - * - * @param src the source code representing the body. - * It must be a single statement or block. - * If it is null, the substituted - * body does nothing except returning zero or null. - */ - public void setBody(String src) throws CannotCompileException { - setBody(src, null, null); - } - - /** - * Sets a method/constructor body. - * - * @param src the source code representing the body. - * It must be a single statement or block. - * If it is null, the substituted - * body does nothing except returning zero or null. - * @param delegateObj the source text specifying the object - * that is called on by $proceed(). - * @param delegateMethod the name of the method - * that is called by $proceed(). - */ - public void setBody(String src, - String delegateObj, String delegateMethod) - throws CannotCompileException - { - CtClass cc = declaringClass; - cc.checkModify(); - try { - Javac jv = new Javac(cc); - if (delegateMethod != null) - jv.recordProceed(delegateObj, delegateMethod); - - Bytecode b = jv.compileBody(this, src); - methodInfo.setCodeAttribute(b.toCodeAttribute()); - methodInfo.setAccessFlags(methodInfo.getAccessFlags() - & ~AccessFlag.ABSTRACT); - methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); - declaringClass.rebuildClassFile(); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - static void setBody0(CtClass srcClass, MethodInfo srcInfo, - CtClass destClass, MethodInfo destInfo, - ClassMap map) - throws CannotCompileException - { - destClass.checkModify(); - - map = new ClassMap(map); - map.put(srcClass.getName(), destClass.getName()); - try { - CodeAttribute cattr = srcInfo.getCodeAttribute(); - if (cattr != null) { - ConstPool cp = destInfo.getConstPool(); - CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map); - destInfo.setCodeAttribute(ca); - // a stack map table is copied to destInfo. - } - } - catch (CodeAttribute.RuntimeCopyException e) { - /* the exception may be thrown by copy() in CodeAttribute. - */ - throw new CannotCompileException(e); - } - - destInfo.setAccessFlags(destInfo.getAccessFlags() - & ~AccessFlag.ABSTRACT); - destClass.rebuildClassFile(); - } - - /** - * Obtains an attribute with the given name. - * If that attribute is not found in the class file, this - * method returns null. - * - *

Note that an attribute is a data block specified by - * the class file format. It is not an annotation. - * See {@link javassist.bytecode.AttributeInfo}. - * - * @param name attribute name - */ - public byte[] getAttribute(String name) { - AttributeInfo ai = methodInfo.getAttribute(name); - if (ai == null) - return null; - else - return ai.get(); - } - - /** - * Adds an attribute. The attribute is saved in the class file. - * - *

Note that an attribute is a data block specified by - * the class file format. It is not an annotation. - * See {@link javassist.bytecode.AttributeInfo}. - * - * @param name attribute name - * @param data attribute value - */ - public void setAttribute(String name, byte[] data) { - declaringClass.checkModify(); - methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(), - name, data)); - } - - /** - * Declares to use $cflow for this method/constructor. - * If $cflow is used, the class files modified - * with Javassist requires a support class - * javassist.runtime.Cflow at runtime - * (other Javassist classes are not required at runtime). - * - *

Every $cflow variable is given a unique name. - * For example, if the given name is "Point.paint", - * then the variable is indicated by $cflow(Point.paint). - * - * @param name $cflow name. It can include - * alphabets, numbers, _, - * $, and . (dot). - * - * @see javassist.runtime.Cflow - */ - public void useCflow(String name) throws CannotCompileException { - CtClass cc = declaringClass; - cc.checkModify(); - ClassPool pool = cc.getClassPool(); - String fname; - int i = 0; - while (true) { - fname = "_cflow$" + i++; - try { - cc.getDeclaredField(fname); - } - catch(NotFoundException e) { - break; - } - } - - pool.recordCflow(name, declaringClass.getName(), fname); - try { - CtClass type = pool.get("javassist.runtime.Cflow"); - CtField field = new CtField(type, fname, cc); - field.setModifiers(Modifier.PUBLIC | Modifier.STATIC); - cc.addField(field, CtField.Initializer.byNew(type)); - insertBefore(fname + ".enter();", false); - String src = fname + ".exit();"; - insertAfter(src, true); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - } - - /** - * Declares a new local variable. The scope of this variable is the - * whole method body. The initial value of that variable is not set. - * The declared variable can be accessed in the code snippet inserted - * by insertBefore(), insertAfter(), etc. - * - *

If the second parameter asFinally to - * insertAfter() is true, the declared local variable - * is not visible from the code inserted by insertAfter(). - * - * @param name the name of the variable - * @param type the type of the variable - * @see #insertBefore(String) - * @see #insertAfter(String) - */ - public void addLocalVariable(String name, CtClass type) - throws CannotCompileException - { - declaringClass.checkModify(); - ConstPool cp = methodInfo.getConstPool(); - CodeAttribute ca = methodInfo.getCodeAttribute(); - if (ca == null) - throw new CannotCompileException("no method body"); - - LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( - LocalVariableAttribute.tag); - if (va == null) { - va = new LocalVariableAttribute(cp); - ca.getAttributes().add(va); - } - - int maxLocals = ca.getMaxLocals(); - String desc = Descriptor.of(type); - va.addEntry(0, ca.getCodeLength(), - cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals); - ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc)); - } - - /** - * Inserts a new parameter, which becomes the first parameter. - */ - public void insertParameter(CtClass type) - throws CannotCompileException - { - declaringClass.checkModify(); - String desc = methodInfo.getDescriptor(); - String desc2 = Descriptor.insertParameter(type, desc); - try { - addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - - methodInfo.setDescriptor(desc2); - } - - /** - * Appends a new parameter, which becomes the last parameter. - */ - public void addParameter(CtClass type) - throws CannotCompileException - { - declaringClass.checkModify(); - String desc = methodInfo.getDescriptor(); - String desc2 = Descriptor.appendParameter(type, desc); - int offset = Modifier.isStatic(getModifiers()) ? 0 : 1; - try { - addParameter2(offset + Descriptor.paramSize(desc), type, desc); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - - methodInfo.setDescriptor(desc2); - } - - private void addParameter2(int where, CtClass type, String desc) - throws BadBytecode - { - CodeAttribute ca = methodInfo.getCodeAttribute(); - if (ca != null) { - int size = 1; - char typeDesc = 'L'; - int classInfo = 0; - if (type.isPrimitive()) { - CtPrimitiveType cpt = (CtPrimitiveType)type; - size = cpt.getDataSize(); - typeDesc = cpt.getDescriptor(); - } - else - classInfo = methodInfo.getConstPool().addClassInfo(type); - - ca.insertLocalVar(where, size); - LocalVariableAttribute va - = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag); - if (va != null) - va.shiftIndex(where, size); - - LocalVariableTypeAttribute lvta - = (LocalVariableTypeAttribute)ca.getAttribute(LocalVariableTypeAttribute.tag); - if (lvta != null) - lvta.shiftIndex(where, size); - - StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); - if (smt != null) - smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); - - StackMap sm = (StackMap)ca.getAttribute(StackMap.tag); - if (sm != null) - sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); - } - } - - /** - * Modifies the method/constructor body. - * - * @param converter specifies how to modify. - */ - public void instrument(CodeConverter converter) - throws CannotCompileException - { - declaringClass.checkModify(); - ConstPool cp = methodInfo.getConstPool(); - converter.doit(getDeclaringClass(), methodInfo, cp); - } - - /** - * Modifies the method/constructor body. - * - *

While executing this method, only replace() - * in Expr is available for bytecode modification. - * Other methods such as insertBefore() may collapse - * the bytecode because the ExprEditor loses - * its current position. - * - * @param editor specifies how to modify. - * @see javassist.expr.Expr#replace(String) - * @see #insertBefore(String) - */ - public void instrument(ExprEditor editor) - throws CannotCompileException - { - // if the class is not frozen, - // does not turn the modified flag on. - if (declaringClass.isFrozen()) - declaringClass.checkModify(); - - if (editor.doit(declaringClass, methodInfo)) - declaringClass.checkModify(); - } - - /** - * Inserts bytecode at the beginning of the body. - * - *

If this object represents a constructor, - * the bytecode is inserted before - * a constructor in the super class or this class is called. - * Therefore, the inserted bytecode is subject to constraints described - * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed). - * For example, it cannot access instance fields or methods although - * it may assign a value to an instance field directly declared in this - * class. Accessing static fields and methods is allowed. - * Use insertBeforeBody() in CtConstructor. - * - * @param src the source code representing the inserted bytecode. - * It must be a single statement or block. - * @see CtConstructor#insertBeforeBody(String) - */ - public void insertBefore(String src) throws CannotCompileException { - insertBefore(src, true); - } - - private void insertBefore(String src, boolean rebuild) - throws CannotCompileException - { - CtClass cc = declaringClass; - cc.checkModify(); - CodeAttribute ca = methodInfo.getCodeAttribute(); - if (ca == null) - throw new CannotCompileException("no method body"); - - CodeIterator iterator = ca.iterator(); - Javac jv = new Javac(cc); - try { - int nvars = jv.recordParams(getParameterTypes(), - Modifier.isStatic(getModifiers())); - jv.recordParamNames(ca, nvars); - jv.recordLocalVariables(ca, 0); - jv.recordType(getReturnType0()); - jv.compileStmnt(src); - Bytecode b = jv.getBytecode(); - int stack = b.getMaxStack(); - int locals = b.getMaxLocals(); - - if (stack > ca.getMaxStack()) - ca.setMaxStack(stack); - - if (locals > ca.getMaxLocals()) - ca.setMaxLocals(locals); - - int pos = iterator.insertEx(b.get()); - iterator.insert(b.getExceptionTable(), pos); - if (rebuild) - methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - /** - * Inserts bytecode at the end of the body. - * The bytecode is inserted just before every return insturction. - * It is not executed when an exception is thrown. - * - * @param src the source code representing the inserted bytecode. - * It must be a single statement or block. - */ - public void insertAfter(String src) - throws CannotCompileException - { - insertAfter(src, false); - } - - /** - * Inserts bytecode at the end of the body. - * The bytecode is inserted just before every return insturction. - * - * @param src the source code representing the inserted bytecode. - * It must be a single statement or block. - * @param asFinally true if the inserted bytecode is executed - * not only when the control normally returns - * but also when an exception is thrown. - * If this parameter is true, the inserted code cannot - * access local variables. - */ - public void insertAfter(String src, boolean asFinally) - throws CannotCompileException - { - CtClass cc = declaringClass; - cc.checkModify(); - ConstPool pool = methodInfo.getConstPool(); - CodeAttribute ca = methodInfo.getCodeAttribute(); - if (ca == null) - throw new CannotCompileException("no method body"); - - CodeIterator iterator = ca.iterator(); - int retAddr = ca.getMaxLocals(); - Bytecode b = new Bytecode(pool, 0, retAddr + 1); - b.setStackDepth(ca.getMaxStack() + 1); - Javac jv = new Javac(b, cc); - try { - int nvars = jv.recordParams(getParameterTypes(), - Modifier.isStatic(getModifiers())); - jv.recordParamNames(ca, nvars); - CtClass rtype = getReturnType0(); - int varNo = jv.recordReturnType(rtype, true); - jv.recordLocalVariables(ca, 0); - - // finally clause for exceptions - int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo, - jv, src); - int handlerPos = iterator.getCodeLength(); - if (asFinally) - ca.getExceptionTable().add(getStartPosOfBody(ca), handlerPos, handlerPos, 0); - - int adviceLen = 0; - int advicePos = 0; - boolean noReturn = true; - while (iterator.hasNext()) { - int pos = iterator.next(); - if (pos >= handlerPos) - break; - - int c = iterator.byteAt(pos); - if (c == Opcode.ARETURN || c == Opcode.IRETURN - || c == Opcode.FRETURN || c == Opcode.LRETURN - || c == Opcode.DRETURN || c == Opcode.RETURN) { - if (noReturn) { - // finally clause for normal termination - adviceLen = insertAfterAdvice(b, jv, src, pool, rtype, varNo); - handlerPos = iterator.append(b.get()); - iterator.append(b.getExceptionTable(), handlerPos); - advicePos = iterator.getCodeLength() - adviceLen; - handlerLen = advicePos - handlerPos; - noReturn = false; - } - insertGoto(iterator, advicePos, pos); - advicePos = iterator.getCodeLength() - adviceLen; - handlerPos = advicePos - handlerLen; - } - } - - if (noReturn) { - handlerPos = iterator.append(b.get()); - iterator.append(b.getExceptionTable(), handlerPos); - } - - ca.setMaxStack(b.getMaxStack()); - ca.setMaxLocals(b.getMaxLocals()); - methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - private int insertAfterAdvice(Bytecode code, Javac jv, String src, - ConstPool cp, CtClass rtype, int varNo) - throws CompileError - { - int pc = code.currentPc(); - if (rtype == CtClass.voidType) { - code.addOpcode(Opcode.ACONST_NULL); - code.addAstore(varNo); - jv.compileStmnt(src); - code.addOpcode(Opcode.RETURN); - if (code.getMaxLocals() < 1) - code.setMaxLocals(1); - } - else { - code.addStore(varNo, rtype); - jv.compileStmnt(src); - code.addLoad(varNo, rtype); - if (rtype.isPrimitive()) - code.addOpcode(((CtPrimitiveType)rtype).getReturnOp()); - else - code.addOpcode(Opcode.ARETURN); - } - - return code.currentPc() - pc; - } - - /* - * assert subr > pos - */ - private void insertGoto(CodeIterator iterator, int subr, int pos) - throws BadBytecode - { - iterator.setMark(subr); - // the gap length might be a multiple of 4. - iterator.writeByte(Opcode.NOP, pos); - boolean wide = subr + 2 - pos > Short.MAX_VALUE; - int len = wide ? 4 : 2; - CodeIterator.Gap gap = iterator.insertGapAt(pos, len, false); - pos = gap.position + gap.length - len; - int offset = iterator.getMark() - pos; - if (wide) { - iterator.writeByte(Opcode.GOTO_W, pos); - iterator.write32bit(offset, pos + 1); - } - else if (offset <= Short.MAX_VALUE) { - iterator.writeByte(Opcode.GOTO, pos); - iterator.write16bit(offset, pos + 1); - } - else { - if (gap.length < 4) { - CodeIterator.Gap gap2 = iterator.insertGapAt(gap.position, 2, false); - pos = gap2.position + gap2.length + gap.length - 4; - } - - iterator.writeByte(Opcode.GOTO_W, pos); - iterator.write32bit(iterator.getMark() - pos, pos + 1); - } - } - - /* insert a finally clause - */ - private int insertAfterHandler(boolean asFinally, Bytecode b, - CtClass rtype, int returnVarNo, - Javac javac, String src) - throws CompileError - { - if (!asFinally) - return 0; - - int var = b.getMaxLocals(); - b.incMaxLocals(1); - int pc = b.currentPc(); - b.addAstore(var); // store an exception - if (rtype.isPrimitive()) { - char c = ((CtPrimitiveType)rtype).getDescriptor(); - if (c == 'D') { - b.addDconst(0.0); - b.addDstore(returnVarNo); - } - else if (c == 'F') { - b.addFconst(0); - b.addFstore(returnVarNo); - } - else if (c == 'J') { - b.addLconst(0); - b.addLstore(returnVarNo); - } - else if (c == 'V') { - b.addOpcode(Opcode.ACONST_NULL); - b.addAstore(returnVarNo); - } - else { // int, boolean, char, short, ... - b.addIconst(0); - b.addIstore(returnVarNo); - } - } - else { - b.addOpcode(Opcode.ACONST_NULL); - b.addAstore(returnVarNo); - } - - javac.compileStmnt(src); - b.addAload(var); - b.addOpcode(Opcode.ATHROW); - return b.currentPc() - pc; - } - - /* -- OLD version -- - - public void insertAfter(String src) throws CannotCompileException { - declaringClass.checkModify(); - CodeAttribute ca = methodInfo.getCodeAttribute(); - CodeIterator iterator = ca.iterator(); - Bytecode b = new Bytecode(methodInfo.getConstPool(), - ca.getMaxStack(), ca.getMaxLocals()); - b.setStackDepth(ca.getMaxStack()); - Javac jv = new Javac(b, declaringClass); - try { - jv.recordParams(getParameterTypes(), - Modifier.isStatic(getModifiers())); - CtClass rtype = getReturnType0(); - int varNo = jv.recordReturnType(rtype, true); - boolean isVoid = rtype == CtClass.voidType; - if (isVoid) { - b.addOpcode(Opcode.ACONST_NULL); - b.addAstore(varNo); - jv.compileStmnt(src); - } - else { - b.addStore(varNo, rtype); - jv.compileStmnt(src); - b.addLoad(varNo, rtype); - } - - byte[] code = b.get(); - ca.setMaxStack(b.getMaxStack()); - ca.setMaxLocals(b.getMaxLocals()); - while (iterator.hasNext()) { - int pos = iterator.next(); - int c = iterator.byteAt(pos); - if (c == Opcode.ARETURN || c == Opcode.IRETURN - || c == Opcode.FRETURN || c == Opcode.LRETURN - || c == Opcode.DRETURN || c == Opcode.RETURN) - iterator.insert(pos, code); - } - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - */ - - /** - * Adds a catch clause that handles an exception thrown in the - * body. The catch clause must end with a return or throw statement. - * - * @param src the source code representing the catch clause. - * It must be a single statement or block. - * @param exceptionType the type of the exception handled by the - * catch clause. - */ - public void addCatch(String src, CtClass exceptionType) - throws CannotCompileException - { - addCatch(src, exceptionType, "$e"); - } - - /** - * Adds a catch clause that handles an exception thrown in the - * body. The catch clause must end with a return or throw statement. - * - * @param src the source code representing the catch clause. - * It must be a single statement or block. - * @param exceptionType the type of the exception handled by the - * catch clause. - * @param exceptionName the name of the variable containing the - * caught exception, for example, - * $e. - */ - public void addCatch(String src, CtClass exceptionType, - String exceptionName) - throws CannotCompileException - { - CtClass cc = declaringClass; - cc.checkModify(); - ConstPool cp = methodInfo.getConstPool(); - CodeAttribute ca = methodInfo.getCodeAttribute(); - CodeIterator iterator = ca.iterator(); - Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals()); - b.setStackDepth(1); - Javac jv = new Javac(b, cc); - try { - jv.recordParams(getParameterTypes(), - Modifier.isStatic(getModifiers())); - int var = jv.recordVariable(exceptionType, exceptionName); - b.addAstore(var); - jv.compileStmnt(src); - - int stack = b.getMaxStack(); - int locals = b.getMaxLocals(); - - if (stack > ca.getMaxStack()) - ca.setMaxStack(stack); - - if (locals > ca.getMaxLocals()) - ca.setMaxLocals(locals); - - int len = iterator.getCodeLength(); - int pos = iterator.append(b.get()); - ca.getExceptionTable().add(getStartPosOfBody(ca), len, len, - cp.addClassInfo(exceptionType)); - iterator.append(b.getExceptionTable(), pos); - methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - /* CtConstructor overrides this method. - */ - int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException { - return 0; - } - - /** - * Inserts bytecode at the specified line in the body. - * It is equivalent to: - * - *
insertAt(lineNum, true, src) - * - *
See this method as well. - * - * @param lineNum the line number. The bytecode is inserted at the - * beginning of the code at the line specified by this - * line number. - * @param src the source code representing the inserted bytecode. - * It must be a single statement or block. - * @return the line number at which the bytecode has been inserted. - * - * @see CtBehavior#insertAt(int,boolean,String) - */ - public int insertAt(int lineNum, String src) - throws CannotCompileException - { - return insertAt(lineNum, true, src); - } - - /** - * Inserts bytecode at the specified line in the body. - * - *

If there is not - * a statement at the specified line, the bytecode might be inserted - * at the line including the first statement after that line specified. - * For example, if there is only a closing brace at that line, the - * bytecode would be inserted at another line below. - * To know exactly where the bytecode will be inserted, call with - * modify set to false. - * - * @param lineNum the line number. The bytecode is inserted at the - * beginning of the code at the line specified by this - * line number. - * @param modify if false, this method does not insert the bytecode. - * It instead only returns the line number at which - * the bytecode would be inserted. - * @param src the source code representing the inserted bytecode. - * It must be a single statement or block. - * If modify is false, the value of src can be null. - * @return the line number at which the bytecode has been inserted. - */ - public int insertAt(int lineNum, boolean modify, String src) - throws CannotCompileException - { - CodeAttribute ca = methodInfo.getCodeAttribute(); - if (ca == null) - throw new CannotCompileException("no method body"); - - LineNumberAttribute ainfo - = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); - if (ainfo == null) - throw new CannotCompileException("no line number info"); - - LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum); - lineNum = pc.line; - int index = pc.index; - if (!modify) - return lineNum; - - CtClass cc = declaringClass; - cc.checkModify(); - CodeIterator iterator = ca.iterator(); - Javac jv = new Javac(cc); - try { - jv.recordLocalVariables(ca, index); - jv.recordParams(getParameterTypes(), - Modifier.isStatic(getModifiers())); - jv.setMaxLocals(ca.getMaxLocals()); - jv.compileStmnt(src); - Bytecode b = jv.getBytecode(); - int locals = b.getMaxLocals(); - int stack = b.getMaxStack(); - ca.setMaxLocals(locals); - - /* We assume that there is no values in the operand stack - * at the position where the bytecode is inserted. - */ - if (stack > ca.getMaxStack()) - ca.setMaxStack(stack); - - index = iterator.insertAt(index, b.get()); - iterator.insert(b.getExceptionTable(), index); - methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); - return lineNum; - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/CtClass.java b/src/com/wenshuo/agent/javassist/CtClass.java deleted file mode 100644 index 21fe5ac..0000000 --- a/src/com/wenshuo/agent/javassist/CtClass.java +++ /dev/null @@ -1,1595 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.URL; -import java.security.ProtectionDomain; -import java.util.Collection; - -import com.wenshuo.agent.javassist.bytecode.ClassFile; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.bytecode.Opcode; -import com.wenshuo.agent.javassist.expr.ExprEditor; - -/* Note: - * - * This class is an abstract class and several methods just return null - * or throw an exception. Those methods are overridden in subclasses - * of this class. Read the source code of CtClassType if you are - * interested in the implementation of Javassist. - * - * Subclasses of CtClass are CtClassType, CtPrimitiveType, and CtArray. - */ - -/** - * An instance of CtClass represents a class. - * It is obtained from ClassPool. - * - * @see ClassPool#get(String) - */ -public abstract class CtClass { - protected String qualifiedName; - - /** - * If the value of this field is not null, then all class - * files modified by Javassist are saved under the directory - * specified by this variable. For example, if the value is - * "./debug", then all class files are saved - * there. The directory name must not end with a directory - * separator such as /. - * - *

The default value is null. - * - * @see #debugWriteFile(String) - * @since 3.16 - */ - public static String debugDump = null; - - /** - * The version number of this release. - */ - public static final String version = "3.20.0-GA"; - - /** - * Prints the version number and the copyright notice. - * - *

The following command invokes this method: - * - *

java -jar javassist.jar
- */ - public static void main(String[] args) { - System.out.println("Javassist version " + CtClass.version); - System.out.println("Copyright (C) 1999-2015 Shigeru Chiba." - + " All Rights Reserved."); - } - - static final String javaLangObject = "java.lang.Object"; - - /** - * The CtClass object representing - * the boolean type. - */ - public static CtClass booleanType; - - /** - * The CtClass object representing - * the char type. - */ - public static CtClass charType; - - /** - * The CtClass object representing - * the byte type. - */ - public static CtClass byteType; - - /** - * The CtClass object representing - * the short type. - */ - public static CtClass shortType; - - /** - * The CtClass object representing - * the int type. - */ - public static CtClass intType; - - /** - * The CtClass object representing - * the long type. - */ - public static CtClass longType; - - /** - * The CtClass object representing - * the float type. - */ - public static CtClass floatType; - - /** - * The CtClass object representing - * the double type. - */ - public static CtClass doubleType; - - /** - * The CtClass object representing - * the void type. - */ - public static CtClass voidType; - - static CtClass[] primitiveTypes; - - static { - primitiveTypes = new CtClass[9]; - - booleanType = - new CtPrimitiveType("boolean", 'Z', "java.lang.Boolean", - "booleanValue", "()Z", Opcode.IRETURN, - Opcode.T_BOOLEAN, 1); - primitiveTypes[0] = booleanType; - - charType = new CtPrimitiveType("char", 'C', "java.lang.Character", - "charValue", "()C", Opcode.IRETURN, - Opcode.T_CHAR, 1); - primitiveTypes[1] = charType; - - byteType = new CtPrimitiveType("byte", 'B', "java.lang.Byte", - "byteValue", "()B", Opcode.IRETURN, - Opcode.T_BYTE, 1); - primitiveTypes[2] = byteType; - - shortType = new CtPrimitiveType("short", 'S', "java.lang.Short", - "shortValue", "()S", Opcode.IRETURN, - Opcode.T_SHORT, 1); - primitiveTypes[3] = shortType; - - intType = new CtPrimitiveType("int", 'I', "java.lang.Integer", - "intValue", "()I", Opcode.IRETURN, - Opcode.T_INT, 1); - primitiveTypes[4] = intType; - - longType = new CtPrimitiveType("long", 'J', "java.lang.Long", - "longValue", "()J", Opcode.LRETURN, - Opcode.T_LONG, 2); - primitiveTypes[5] = longType; - - floatType = new CtPrimitiveType("float", 'F', "java.lang.Float", - "floatValue", "()F", Opcode.FRETURN, - Opcode.T_FLOAT, 1); - primitiveTypes[6] = floatType; - - doubleType = new CtPrimitiveType("double", 'D', "java.lang.Double", - "doubleValue", "()D", Opcode.DRETURN, - Opcode.T_DOUBLE, 2); - primitiveTypes[7] = doubleType; - - voidType = new CtPrimitiveType("void", 'V', "java.lang.Void", - null, null, Opcode.RETURN, 0, 0); - primitiveTypes[8] = voidType; - } - - protected CtClass(String name) { - qualifiedName = name; - } - - /** - * Converts the object to a string. - */ - public String toString() { - StringBuffer buf = new StringBuffer(getClass().getName()); - buf.append("@"); - buf.append(Integer.toHexString(hashCode())); - buf.append("["); - extendToString(buf); - buf.append("]"); - return buf.toString(); - } - - /** - * Implemented in subclasses to add to the {@link #toString()} result. - * Subclasses should put a space before each token added to the buffer. - */ - protected void extendToString(StringBuffer buffer) { - buffer.append(getName()); - } - - /** - * Returns a ClassPool for this class. - */ - public ClassPool getClassPool() { return null; } - - /** - * Returns a class file for this class. - * - *

This method is not available if isFrozen() - * is true. - */ - public ClassFile getClassFile() { - checkModify(); - return getClassFile2(); - } - - /** - * Returns a class file for this class (read only). - * Normal applications do not need calling this method. Use - * getClassFile(). - * - *

The ClassFile object obtained by this method - * is read only. Changes to this object might not be reflected - * on a class file generated by toBytecode(), - * toClass(), etc. - * - *

This method is available even if isFrozen() - * is true. However, if the class is frozen, it might be also - * pruned. - * - * @see CtClass#getClassFile() - * @see CtClass#isFrozen() - * @see CtClass#prune() - */ - public ClassFile getClassFile2() { return null; } - - /** - * Undocumented method. Do not use; internal-use only. - */ - public com.wenshuo.agent.javassist.compiler.AccessorMaker getAccessorMaker() { - return null; - } - - /** - * Returns the uniform resource locator (URL) of the class file. - */ - public URL getURL() throws NotFoundException { - throw new NotFoundException(getName()); - } - - /** - * Returns true if the definition of the class has been modified. - */ - public boolean isModified() { return false; } - - /** - * Returns true if the class has been loaded or written out - * and thus it cannot be modified any more. - * - * @see #defrost() - * @see #detach() - */ - public boolean isFrozen() { return true; } - - /** - * Makes the class frozen. - * - * @see #isFrozen() - * @see #defrost() - * @since 3.6 - */ - public void freeze() {} - - /* Note: this method is overridden by CtClassType - */ - void checkModify() throws RuntimeException { - if (isFrozen()) - throw new RuntimeException(getName() + " class is frozen"); - - // isModified() must return true after this method is invoked. - } - - /** - * Defrosts the class so that the class can be modified again. - * - *

To avoid changes that will be never reflected, - * the class is frozen to be unmodifiable if it is loaded or - * written out. This method should be called only in a case - * that the class will be reloaded or written out later again. - * - *

If defrost() will be called later, pruning - * must be disallowed in advance. - * - * @see #isFrozen() - * @see #stopPruning(boolean) - * @see #detach() - */ - public void defrost() { - throw new RuntimeException("cannot defrost " + getName()); - } - - /** - * Returns true if this object represents a primitive - * Java type: boolean, byte, char, short, int, long, float, double, - * or void. - */ - public boolean isPrimitive() { return false; } - - /** - * Returns true if this object represents an array type. - */ - public boolean isArray() { - return false; - } - - /** - * If this object represents an array, this method returns the component - * type of the array. Otherwise, it returns null. - */ - public CtClass getComponentType() throws NotFoundException { - return null; - } - - /** - * Returns true if this class extends or implements - * clazz. It also returns true if - * this class is the same as clazz. - */ - public boolean subtypeOf(CtClass clazz) throws NotFoundException { - return this == clazz || getName().equals(clazz.getName()); - } - - /** - * Obtains the fully-qualified name of the class. - */ - public String getName() { return qualifiedName; } - - /** - * Obtains the not-qualified class name. - */ - public final String getSimpleName() { - String qname = qualifiedName; - int index = qname.lastIndexOf('.'); - if (index < 0) - return qname; - else - return qname.substring(index + 1); - } - - /** - * Obtains the package name. It may be null. - */ - public final String getPackageName() { - String qname = qualifiedName; - int index = qname.lastIndexOf('.'); - if (index < 0) - return null; - else - return qname.substring(0, index); - } - - /** - * Sets the class name - * - * @param name fully-qualified name - */ - public void setName(String name) { - checkModify(); - if (name != null) - qualifiedName = name; - } - - /** - * Returns the generic signature of the class. - * - *

The generics of Java is implemented by the erasure technique. - * After compilation, all type parameters are dropped off from the - * main part of a class file. However, for reflection, the type - * parameters are encoded into generic signatures and attached - * to a class file. - * - * @return null if the generic signature is not included. - * @see javassist.bytecode.SignatureAttribute#toClassSignature(String) - * @see CtMember#getGenericSignature() - * @since 3.17 - */ - public String getGenericSignature() { return null; } - - /** - * Sets the generic signature of the class. - * - *

The generics of Java is implemented by the erasure technique. - * After compilation, all type parameters are dropped off from the - * main part of a class file. However, for reflection, the type - * parameters must be encoded into generic signatures and attached - * to a class file. - * - *

For example, - * - *

class List<T> {
-     *     T value;
-     *     T get() { return value; }
-     *     void set(T v) { value = v; }
-     * }
-     * 
- * - *

this class is generated by the following code: - * - *

-     * ClassPool pool = ClassPool.getDefault();
-     * CtClass cc = pool.makeClass("List");
-     * CtClass objectClass = pool.get(CtClass.javaLangObject);
-     * ClassSignature cs = new ClassSignature(
-     *                         new TypeParameter[] { new TypeParameter("T") });
-     * cc.setGenericSignature(cs.encode());    // <T:Ljava/lang/Object;>Ljava/lang/Object;
-     *
-     * CtField f = new CtField(objClass, "value", cc);
-     * TypeVariable tvar = new TypeVariable("T");
-     * f.setGenericSignature(tvar.encode());   // TT;
-     * cc.addField(f);
-     *
-     * CtMethod m = CtNewMethod.make("public Object get(){return value;}", cc);
-     * MethodSignature ms = new MethodSignature(null, null, tvar, null);
-     * m.setGenericSignature(ms.encode());     // ()TT;
-     * cc.addMethod(m);
-     *
-     * CtMethod m2 = CtNewMethod.make("public void set(Object v){value = v;}", cc);
-     * MethodSignature ms2 = new MethodSignature(null, new Type[] { tvar },
-     *                                           new BaseType("void"), null);
-     * m2.setGenericSignature(ms2.encode());   // (TT;)V;
-     * cc.addMethod(m2);
-     *
-     * cc.writeFile();
-     * 
- * - *

The generated class file is equivalent to the following: - * - *

class List {
-     *     Object value;
-     *     Object get() { return value; }
-     *     void set(Object v) { value = v; }
-     * }
- * - *

but it includes generic signatures for the class, the field, - * and the methods so that the type variable T can be - * accessible through reflection. - * - *

MethodSignature is a utility class. You can directly - * pass the signature string to the setGenericSignature method. - * For the specification of the signatures, see Section 4.7.9.1 Signatures - * of The Java Virtual Machine Specification (Java SE 8). - * - * @param sig a generic signature. - * @see javassist.bytecode.SignatureAttribute.ClassSignature#encode() - * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode() - * @see CtMember#setGenericSignature(String) - * @since 3.17 - */ - public void setGenericSignature(String sig) { checkModify(); } - - /** - * Substitutes newName for all occurrences of a class - * name oldName in the class file. - * - * @param oldName replaced class name - * @param newName substituted class name - */ - public void replaceClassName(String oldName, String newName) { - checkModify(); - } - - /** - * Changes class names appearing in the class file according to the - * given map. - * - *

All the class names appearing in the class file are tested - * with map to determine whether each class name is - * replaced or not. Thus this method can be used for collecting - * all the class names in the class file. To do that, first define - * a subclass of ClassMap so that get() - * records all the given parameters. Then, make an instance of - * that subclass as an empty hash-table. Finally, pass that instance - * to this method. After this method finishes, that instance would - * contain all the class names appearing in the class file. - * - * @param map the hashtable associating replaced class names - * with substituted names. - */ - public void replaceClassName(ClassMap map) { - checkModify(); - } - - /** - * Returns a collection of the names of all the classes - * referenced in this class. - * That collection includes the name of this class. - * - *

This method may return null. - * - * @return a Collection<String> object. - */ - public synchronized Collection getRefClasses() { - ClassFile cf = getClassFile2(); - if (cf != null) { - ClassMap cm = new ClassMap() { - public void put(String oldname, String newname) { - put0(oldname, newname); - } - - public Object get(Object jvmClassName) { - String n = toJavaName((String)jvmClassName); - put0(n, n); - return null; - } - - public void fix(String name) {} - }; - cf.getRefClasses(cm); - return cm.values(); - } - else - return null; - } - - /** - * Determines whether this object represents a class or an interface. - * It returns true if this object represents an interface. - */ - public boolean isInterface() { - return false; - } - - /** - * Determines whether this object represents an annotation type. - * It returns true if this object represents an annotation type. - * - * @since 3.2 - */ - public boolean isAnnotation() { - return false; - } - - /** - * Determines whether this object represents an enum. - * It returns true if this object represents an enum. - * - * @since 3.2 - */ - public boolean isEnum() { - return false; - } - - /** - * Returns the modifiers for this class, encoded in an integer. - * For decoding, use javassist.Modifier. - * - *

If the class is a static nested class (a.k.a. static inner class), - * the returned modifiers include Modifier.STATIC. - * - * @see Modifier - */ - public int getModifiers() { - return 0; - } - - /** - * Returns true if the class has the specified annotation class. - * - * @param clz the annotation class. - * @return true if the annotation is found, otherwise false. - * @since 3.11 - */ - public boolean hasAnnotation(Class clz) { - return false; - } - - /** - * Returns the annotation if the class has the specified annotation class. - * For example, if an annotation @Author is associated - * with this class, an Author object is returned. - * The member values can be obtained by calling methods on - * the Author object. - * - * @param clz the annotation class. - * @return the annotation if found, otherwise null. - * @since 3.11 - */ - public Object getAnnotation(Class clz) throws ClassNotFoundException { - return null; - } - - /** - * Returns the annotations associated with this class. - * For example, if an annotation @Author is associated - * with this class, the returned array contains an Author - * object. The member values can be obtained by calling methods on - * the Author object. - * - * @return an array of annotation-type objects. - * @see CtMember#getAnnotations() - * @since 3.1 - */ - public Object[] getAnnotations() throws ClassNotFoundException { - return new Object[0]; - } - - /** - * Returns the annotations associated with this class. - * This method is equivalent to getAnnotations() - * except that, if any annotations are not on the classpath, - * they are not included in the returned array. - * - * @return an array of annotation-type objects. - * @see #getAnnotations() - * @see CtMember#getAvailableAnnotations() - * @since 3.3 - */ - public Object[] getAvailableAnnotations(){ - return new Object[0]; - } - - /** - * Returns an array of nested classes declared in the class. - * Nested classes are inner classes, anonymous classes, local classes, - * and static nested classes. This simply calls getNestedClasses(). - * - * @see #getNestedClasses() - * @since 3.15 - */ - public CtClass[] getDeclaredClasses() throws NotFoundException { - return getNestedClasses(); - } - - /** - * Returns an array of nested classes declared in the class. - * Nested classes are inner classes, anonymous classes, local classes, - * and static nested classes. - * - * @since 3.2 - */ - public CtClass[] getNestedClasses() throws NotFoundException { - return new CtClass[0]; - } - - /** - * Sets the modifiers. - * - *

If the class is a nested class, this method also modifies - * the class declaring that nested class (i.e. the enclosing - * class is modified). - * - * @param mod modifiers encoded by - * javassist.Modifier - * @see Modifier - */ - public void setModifiers(int mod) { - checkModify(); - } - - /** - * Determines whether the class directly or indirectly extends - * the given class. If this class extends a class A and - * the class A extends a class B, then subclassof(B) returns true. - * - *

This method returns true if the given class is identical to - * the class represented by this object. - */ - public boolean subclassOf(CtClass superclass) { - return false; - } - - /** - * Obtains the class object representing the superclass of the - * class. - * It returns null if this object represents the - * java.lang.Object class and thus it does not have - * the super class. - * - *

If this object represents an interface, this method - * always returns the java.lang.Object class. - * To obtain the super interfaces - * extended by that interface, call getInterfaces(). - */ - public CtClass getSuperclass() throws NotFoundException { - return null; - } - - /** - * Changes a super class unless this object represents an interface. - * The new super class must be compatible with the old one; for example, - * it should inherit from the old super class. - * - *

If this object represents an interface, this method is equivalent - * to addInterface(); it appends clazz to - * the list of the super interfaces extended by that interface. - * Note that an interface can extend multiple super interfaces. - * - * @see #replaceClassName(String, String) - * @see #replaceClassName(ClassMap) - */ - public void setSuperclass(CtClass clazz) throws CannotCompileException { - checkModify(); - } - - /** - * Obtains the class objects representing the interfaces implemented - * by the class or, if this object represents an interface, the interfaces - * extended by that interface. - */ - public CtClass[] getInterfaces() throws NotFoundException { - return new CtClass[0]; - } - - /** - * Sets implemented interfaces. If this object represents an interface, - * this method sets the interfaces extended by that interface. - * - * @param list a list of the CtClass objects - * representing interfaces, or - * null if the class implements - * no interfaces. - */ - public void setInterfaces(CtClass[] list) { - checkModify(); - } - - /** - * Adds an interface. - * - * @param anInterface the added interface. - */ - public void addInterface(CtClass anInterface) { - checkModify(); - } - - /** - * If this class is a member class or interface of another class, - * then the class enclosing this class is returned. - * - * @return null if this class is a top-level class or an anonymous class. - */ - public CtClass getDeclaringClass() throws NotFoundException { - return null; - } - - /** - * Returns the immediately enclosing method of this class. - * This method works only with JDK 1.5 or later. - * - * @return null if this class is not a local class or an anonymous - * class. - * @deprecated The enclosing method might be a constructor. - * Use {@link #getEnclosingBehavior()}. - * @see #getEnclosingBehavior() - */ - public final CtMethod getEnclosingMethod() throws NotFoundException { - CtBehavior b = getEnclosingBehavior(); - if (b == null) - return null; - else if (b instanceof CtMethod) - return (CtMethod)b; - else - throw new NotFoundException(b.getLongName() + " is enclosing " + getName()); - } - - /** - * Returns the immediately enclosing method of this class. - * It might be not a method but a constructor. - * This method works only with JDK 1.5 or later. - * - * @return null if this class is not a local class or an anonymous - * class. - */ - public CtBehavior getEnclosingBehavior() throws NotFoundException { - return null; - } - - /** - * Makes a new public nested class. If this method is called, - * the CtClass, which encloses the nested class, is modified - * since a class file includes a list of nested classes. - * - *

The current implementation only supports a static nested class. - * isStatic must be true. - * - * @param name the simple name of the nested class. - * @param isStatic true if the nested class is static. - */ - public CtClass makeNestedClass(String name, boolean isStatic) { - throw new RuntimeException(getName() + " is not a class"); - } - - /** - * Returns an array containing CtField objects - * representing all the non-private fields of the class. - * That array includes non-private fields inherited from the - * superclasses. - */ - public CtField[] getFields() { return new CtField[0]; } - - /** - * Returns the field with the specified name. The returned field - * may be a private field declared in a super class or interface. - */ - public CtField getField(String name) throws NotFoundException { - return getField(name, null); - } - - /** - * Returns the field with the specified name and type. The returned field - * may be a private field declared in a super class or interface. - * Unlike Java, the JVM allows a class to have - * multiple fields with the same name but different types. - * - * @param name the field name. - * @param desc the type descriptor of the field. It is available by - * {@link CtField#getSignature()}. - * @see CtField#getSignature() - */ - public CtField getField(String name, String desc) throws NotFoundException { - throw new NotFoundException(name); - } - - /** - * @return null if the specified field is not found. - */ - CtField getField2(String name, String desc) { return null; } - - /** - * Gets all the fields declared in the class. The inherited fields - * are not included. - * - *

Note: the result does not include inherited fields. - */ - public CtField[] getDeclaredFields() { return new CtField[0]; } - - /** - * Retrieves the field with the specified name among the fields - * declared in the class. - * - *

Note: this method does not search the super classes. - */ - public CtField getDeclaredField(String name) throws NotFoundException { - throw new NotFoundException(name); - } - - /** - * Retrieves the field with the specified name and type among the fields - * declared in the class. Unlike Java, the JVM allows a class to have - * multiple fields with the same name but different types. - * - *

Note: this method does not search the super classes. - * - * @param name the field name. - * @param desc the type descriptor of the field. It is available by - * {@link CtField#getSignature()}. - * @see CtField#getSignature() - */ - public CtField getDeclaredField(String name, String desc) throws NotFoundException { - throw new NotFoundException(name); - } - - /** - * Gets all the constructors and methods declared in the class. - */ - public CtBehavior[] getDeclaredBehaviors() { - return new CtBehavior[0]; - } - - /** - * Returns an array containing CtConstructor objects - * representing all the non-private constructors of the class. - */ - public CtConstructor[] getConstructors() { - return new CtConstructor[0]; - } - - /** - * Returns the constructor with the given signature, - * which is represented by a character string - * called method descriptor. - * For details of the method descriptor, see the JVM specification - * or javassist.bytecode.Descriptor. - * - * @param desc method descriptor - * @see javassist.bytecode.Descriptor - */ - public CtConstructor getConstructor(String desc) - throws NotFoundException - { - throw new NotFoundException("no such constructor"); - } - - /** - * Gets all the constructors declared in the class. - * - * @see javassist.CtConstructor - */ - public CtConstructor[] getDeclaredConstructors() { - return new CtConstructor[0]; - } - - /** - * Returns a constructor receiving the specified parameters. - * - * @param params parameter types. - */ - public CtConstructor getDeclaredConstructor(CtClass[] params) - throws NotFoundException - { - String desc = Descriptor.ofConstructor(params); - return getConstructor(desc); - } - - /** - * Gets the class initializer (static constructor) - * declared in the class. - * This method returns null if - * no class initializer is not declared. - * - * @see #makeClassInitializer() - * @see javassist.CtConstructor - */ - public CtConstructor getClassInitializer() { - return null; - } - - /** - * Returns an array containing CtMethod objects - * representing all the non-private methods of the class. - * That array includes non-private methods inherited from the - * superclasses. - */ - public CtMethod[] getMethods() { - return new CtMethod[0]; - } - - /** - * Returns the method with the given name and signature. - * The returned method may be declared in a super class. - * The method signature is represented by a character string - * called method descriptor, - * which is defined in the JVM specification. - * - * @param name method name - * @param desc method descriptor - * @see CtBehavior#getSignature() - * @see javassist.bytecode.Descriptor - */ - public CtMethod getMethod(String name, String desc) - throws NotFoundException - { - throw new NotFoundException(name); - } - - /** - * Gets all methods declared in the class. The inherited methods - * are not included. - * - * @see javassist.CtMethod - */ - public CtMethod[] getDeclaredMethods() { - return new CtMethod[0]; - } - - /** - * Retrieves the method with the specified name and parameter types - * among the methods declared in the class. - * - *

Note: this method does not search the superclasses. - * - * @param name method name - * @param params parameter types - * @see javassist.CtMethod - */ - public CtMethod getDeclaredMethod(String name, CtClass[] params) - throws NotFoundException - { - throw new NotFoundException(name); - } - - /** - * Retrieves methods with the specified name among the methods - * declared in the class. Multiple methods with different parameters - * may be returned. - * - *

Note: this method does not search the superclasses.

- * - * @param name method name. - * @since 3.19 - */ - public CtMethod[] getDeclaredMethods(String name) throws NotFoundException { - throw new NotFoundException(name); - } - - /** - * Retrieves the method with the specified name among the methods - * declared in the class. If there are multiple methods with - * the specified name, then this method returns one of them. - * - *

Note: this method does not search the superclasses. - * - * @see javassist.CtMethod - */ - public CtMethod getDeclaredMethod(String name) throws NotFoundException { - throw new NotFoundException(name); - } - - /** - * Makes an empty class initializer (static constructor). - * If the class already includes a class initializer, - * this method returns it. - * - * @see #getClassInitializer() - */ - public CtConstructor makeClassInitializer() - throws CannotCompileException - { - throw new CannotCompileException("not a class"); - } - - /** - * Adds a constructor. To add a class initializer (static constructor), - * call makeClassInitializer(). - * - * @see #makeClassInitializer() - */ - public void addConstructor(CtConstructor c) - throws CannotCompileException - { - checkModify(); - } - - /** - * Removes a constructor declared in this class. - * - * @param c removed constructor. - * @throws NotFoundException if the constructor is not found. - */ - public void removeConstructor(CtConstructor c) throws NotFoundException { - checkModify(); - } - - /** - * Adds a method. - */ - public void addMethod(CtMethod m) throws CannotCompileException { - checkModify(); - } - - /** - * Removes a method declared in this class. - * - * @param m removed method. - * @throws NotFoundException if the method is not found. - */ - public void removeMethod(CtMethod m) throws NotFoundException { - checkModify(); - } - - /** - * Adds a field. - * - *

The CtField belonging to another - * CtClass cannot be directly added to this class. - * Only a field created for this class can be added. - * - * @see javassist.CtField#CtField(CtField,CtClass) - */ - public void addField(CtField f) throws CannotCompileException { - addField(f, (CtField.Initializer)null); - } - - /** - * Adds a field with an initial value. - * - *

The CtField belonging to another - * CtClass cannot be directly added to this class. - * Only a field created for this class can be added. - * - *

The initial value is given as an expression written in Java. - * Any regular Java expression can be used for specifying the initial - * value. The followings are examples. - * - *

-     * cc.addField(f, "0")               // the initial value is 0.
-     * cc.addField(f, "i + 1")           // i + 1.
-     * cc.addField(f, "new Point()");    // a Point object.
-     * 
- * - *

Here, the type of variable cc is CtClass. - * The type of f is CtField. - * - *

Note: do not change the modifier of the field - * (in particular, do not add or remove static - * to/from the modifier) - * after it is added to the class by addField(). - * - * @param init an expression for the initial value. - * - * @see javassist.CtField.Initializer#byExpr(String) - * @see javassist.CtField#CtField(CtField,CtClass) - */ - public void addField(CtField f, String init) - throws CannotCompileException - { - checkModify(); - } - - /** - * Adds a field with an initial value. - * - *

The CtField belonging to another - * CtClass cannot be directly added to this class. - * Only a field created for this class can be added. - * - *

For example, - * - *

-     * CtClass cc = ...;
-     * addField(new CtField(CtClass.intType, "i", cc),
-     *          CtField.Initializer.constant(1));
-     * 
- * - *

This code adds an int field named "i". The - * initial value of this field is 1. - * - * @param init specifies the initial value of the field. - * - * @see javassist.CtField#CtField(CtField,CtClass) - */ - public void addField(CtField f, CtField.Initializer init) - throws CannotCompileException - { - checkModify(); - } - - /** - * Removes a field declared in this class. - * - * @param f removed field. - * @throws NotFoundException if the field is not found. - */ - public void removeField(CtField f) throws NotFoundException { - checkModify(); - } - - /** - * Obtains an attribute with the given name. - * If that attribute is not found in the class file, this - * method returns null. - * - *

This is a convenient method mainly for obtaining - * a user-defined attribute. For dealing with attributes, see the - * javassist.bytecode package. For example, the following - * expression returns all the attributes of a class file. - * - *

-     * getClassFile().getAttributes()
-     * 
- * - * @param name attribute name - * @see javassist.bytecode.AttributeInfo - */ - public byte[] getAttribute(String name) { - return null; - } - - /** - * Adds a named attribute. - * An arbitrary data (smaller than 64Kb) can be saved in the class - * file. Some attribute name are reserved by the JVM. - * The attributes with the non-reserved names are ignored when a - * class file is loaded into the JVM. - * If there is already an attribute with - * the same name, this method substitutes the new one for it. - * - *

This is a convenient method mainly for adding - * a user-defined attribute. For dealing with attributes, see the - * javassist.bytecode package. For example, the following - * expression adds an attribute info to a class file. - * - *

-     * getClassFile().addAttribute(info)
-     * 
- * - * @param name attribute name - * @param data attribute value - * @see javassist.bytecode.AttributeInfo - */ - public void setAttribute(String name, byte[] data) { - checkModify(); - } - - /** - * Applies the given converter to all methods and constructors - * declared in the class. This method calls instrument() - * on every CtMethod and CtConstructor object - * in the class. - * - * @param converter specifies how to modify. - */ - public void instrument(CodeConverter converter) - throws CannotCompileException - { - checkModify(); - } - - /** - * Modifies the bodies of all methods and constructors - * declared in the class. This method calls instrument() - * on every CtMethod and CtConstructor object - * in the class. - * - * @param editor specifies how to modify. - */ - public void instrument(ExprEditor editor) - throws CannotCompileException - { - checkModify(); - } - - /** - * Converts this class to a java.lang.Class object. - * Once this method is called, further modifications are not - * allowed any more. - * To load the class, this method uses the context class loader - * of the current thread. If the program is running on some application - * server, the context class loader might be inappropriate to load the - * class. - * - *

This method is provided for convenience. If you need more - * complex functionality, you should write your own class loader. - * - *

Note: this method calls toClass() - * in ClassPool. - * - *

Warining: A Class object returned by this method may not - * work with a security manager or a signed jar file because a - * protection domain is not specified. - * - * @see #toClass(java.lang.ClassLoader,ProtectionDomain) - * @see ClassPool#toClass(CtClass) - */ - public Class toClass() throws CannotCompileException { - return getClassPool().toClass(this); - } - - /** - * Converts this class to a java.lang.Class object. - * Once this method is called, further modifications are not allowed - * any more. - * - *

The class file represented by this CtClass is - * loaded by the given class loader to construct a - * java.lang.Class object. Since a private method - * on the class loader is invoked through the reflection API, - * the caller must have permissions to do that. - * - *

An easy way to obtain ProtectionDomain object is - * to call getProtectionDomain() - * in java.lang.Class. It returns the domain that - * the class belongs to. - * - *

This method is provided for convenience. If you need more - * complex functionality, you should write your own class loader. - * - *

Note: this method calls toClass() - * in ClassPool. - * - * @param loader the class loader used to load this class. - * If it is null, the class loader returned by - * {@link ClassPool#getClassLoader()} is used. - * @param domain the protection domain that the class belongs to. - * If it is null, the default domain created - * by java.lang.ClassLoader is used. - * @see ClassPool#toClass(CtClass,java.lang.ClassLoader) - * @since 3.3 - */ - public Class toClass(ClassLoader loader, ProtectionDomain domain) - throws CannotCompileException - { - ClassPool cp = getClassPool(); - if (loader == null) - loader = cp.getClassLoader(); - - return cp.toClass(this, loader, domain); - } - - /** - * Converts this class to a java.lang.Class object. - * - *

Warining: A Class object returned by this method may not - * work with a security manager or a signed jar file because a - * protection domain is not specified. - * - * @deprecated Replaced by {@link #toClass(ClassLoader,ProtectionDomain)} - */ - public final Class toClass(ClassLoader loader) - throws CannotCompileException - { - return getClassPool().toClass(this, loader); - } - - /** - * Removes this CtClass object from the - * ClassPool. - * After this method is called, any method cannot be called on the - * removed CtClass object. - * - *

If get() in ClassPool is called - * with the name of the removed method, - * the ClassPool will read the class file again - * and constructs another CtClass object representing - * the same class. - */ - public void detach() { - ClassPool cp = getClassPool(); - CtClass obj = cp.removeCached(getName()); - if (obj != this) - cp.cacheCtClass(getName(), obj, false); - } - - /** - * Disallows (or allows) automatically pruning this CtClass - * object. - * - *

- * Javassist can automatically prune a CtClass object - * when toBytecode() (or toClass(), - * writeFile()) is called. - * Since a ClassPool holds all instances of CtClass - * even after toBytecode() (or toClass(), - * writeFile()) is called, pruning may significantly - * save memory consumption. - * - *

If ClassPool.doPruning is true, the automatic pruning - * is on by default. Otherwise, it is off. The default value of - * ClassPool.doPruning is false. - * - * @param stop disallow pruning if true. Otherwise, allow. - * @return the previous status of pruning. true if pruning is already stopped. - * - * @see #detach() - * @see #prune() - * @see ClassPool#doPruning - */ - public boolean stopPruning(boolean stop) { return true; } - - /** - * Discards unnecessary attributes, in particular, - * CodeAttributes (method bodies) of the class, - * to minimize the memory footprint. - * After calling this method, the class is read only. - * It cannot be modified any more. - * Furthermore, toBytecode(), - * writeFile(), toClass(), - * or instrument() cannot be called. - * However, the method names and signatures in the class etc. - * are still accessible. - * - *

toBytecode(), writeFile(), and - * toClass() internally call this method if - * automatic pruning is on. - * - *

According to some experiments, pruning does not really reduce - * memory consumption. Only about 20%. Since pruning takes time, - * it might not pay off. So the automatic pruning is off by default. - * - * @see #stopPruning(boolean) - * @see #detach() - * @see ClassPool#doPruning - * - * @see #toBytecode() - * @see #toClass() - * @see #writeFile() - * @see #instrument(CodeConverter) - * @see #instrument(ExprEditor) - */ - public void prune() {} - - /* Called by get() in ClassPool. - * CtClassType overrides this method. - */ - void incGetCounter() {} - - /** - * If this method is called, the class file will be - * rebuilt when it is finally generated by - * toBytecode() and writeFile(). - * For a performance reason, the symbol table of the - * class file may contain unused entries, for example, - * after a method or a filed is deleted. - * This method - * removes those unused entries. This removal will - * minimize the size of the class file. - * - * @since 3.8.1 - */ - public void rebuildClassFile() {} - - /** - * Converts this class to a class file. - * Once this method is called, further modifications are not - * possible any more. - * - * @return the contents of the class file. - */ - public byte[] toBytecode() throws IOException, CannotCompileException { - ByteArrayOutputStream barray = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(barray); - try { - toBytecode(out); - } - finally { - out.close(); - } - - return barray.toByteArray(); - } - - /** - * Writes a class file represented by this CtClass - * object in the current directory. - * Once this method is called, further modifications are not - * possible any more. - * - * @see #debugWriteFile() - */ - public void writeFile() - throws NotFoundException, IOException, CannotCompileException - { - writeFile("."); - } - - /** - * Writes a class file represented by this CtClass - * object on a local disk. - * Once this method is called, further modifications are not - * possible any more. - * - * @param directoryName it must end without a directory separator. - * @see #debugWriteFile(String) - */ - public void writeFile(String directoryName) - throws CannotCompileException, IOException - { - DataOutputStream out = makeFileOutput(directoryName); - try { - toBytecode(out); - } - finally { - out.close(); - } - } - - protected DataOutputStream makeFileOutput(String directoryName) { - String classname = getName(); - String filename = directoryName + File.separatorChar - + classname.replace('.', File.separatorChar) + ".class"; - int pos = filename.lastIndexOf(File.separatorChar); - if (pos > 0) { - String dir = filename.substring(0, pos); - if (!dir.equals(".")) - new File(dir).mkdirs(); - } - - return new DataOutputStream(new BufferedOutputStream( - new DelayedFileOutputStream(filename))); - } - - /** - * Writes a class file as writeFile() does although this - * method does not prune or freeze the class after writing the class - * file. Note that, once writeFile() or toBytecode() - * is called, it cannot be called again since the class is pruned and frozen. - * This method would be useful for debugging. - */ - public void debugWriteFile() { - debugWriteFile("."); - } - - /** - * Writes a class file as writeFile() does although this - * method does not prune or freeze the class after writing the class - * file. Note that, once writeFile() or toBytecode() - * is called, it cannot be called again since the class is pruned and frozen. - * This method would be useful for debugging. - * - * @param directoryName it must end without a directory separator. - */ - public void debugWriteFile(String directoryName) { - try { - boolean p = stopPruning(true); - writeFile(directoryName); - defrost(); - stopPruning(p); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - static class DelayedFileOutputStream extends OutputStream { - private FileOutputStream file; - private String filename; - - DelayedFileOutputStream(String name) { - file = null; - filename = name; - } - - private void init() throws IOException { - if (file == null) - file = new FileOutputStream(filename); - } - - public void write(int b) throws IOException { - init(); - file.write(b); - } - - public void write(byte[] b) throws IOException { - init(); - file.write(b); - } - - public void write(byte[] b, int off, int len) throws IOException { - init(); - file.write(b, off, len); - - } - - public void flush() throws IOException { - init(); - file.flush(); - } - - public void close() throws IOException { - init(); - file.close(); - } - } - - /** - * Converts this class to a class file. - * Once this method is called, further modifications are not - * possible any more. - * - *

This method dose not close the output stream in the end. - * - * @param out the output stream that a class file is written to. - */ - public void toBytecode(DataOutputStream out) - throws CannotCompileException, IOException - { - throw new CannotCompileException("not a class"); - } - - /** - * Makes a unique member name. This method guarantees that - * the returned name is not used as a prefix of any methods - * or fields visible in this class. - * If the returned name is XYZ, then any method or field names - * in this class do not start with XYZ. - * - * @param prefix the prefix of the member name. - */ - public String makeUniqueName(String prefix) { - throw new RuntimeException("not available in " + getName()); - } - - /* Invoked from ClassPool#compress(). - * This method is overridden by CtClassType. - */ - void compress() {} -} diff --git a/src/com/wenshuo/agent/javassist/CtClassType.java b/src/com/wenshuo/agent/javassist/CtClassType.java deleted file mode 100644 index e7774e5..0000000 --- a/src/com/wenshuo/agent/javassist/CtClassType.java +++ /dev/null @@ -1,1761 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.lang.ref.WeakReference; -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; -import java.util.Set; - -import com.wenshuo.agent.javassist.bytecode.AccessFlag; -import com.wenshuo.agent.javassist.bytecode.AttributeInfo; -import com.wenshuo.agent.javassist.bytecode.AnnotationsAttribute; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.Bytecode; -import com.wenshuo.agent.javassist.bytecode.ClassFile; -import com.wenshuo.agent.javassist.bytecode.CodeAttribute; -import com.wenshuo.agent.javassist.bytecode.ConstantAttribute; -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.bytecode.EnclosingMethodAttribute; -import com.wenshuo.agent.javassist.bytecode.FieldInfo; -import com.wenshuo.agent.javassist.bytecode.InnerClassesAttribute; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; -import com.wenshuo.agent.javassist.bytecode.ParameterAnnotationsAttribute; -import com.wenshuo.agent.javassist.bytecode.SignatureAttribute; -import com.wenshuo.agent.javassist.bytecode.annotation.Annotation; -import com.wenshuo.agent.javassist.compiler.AccessorMaker; -import com.wenshuo.agent.javassist.compiler.CompileError; -import com.wenshuo.agent.javassist.compiler.Javac; -import com.wenshuo.agent.javassist.expr.ExprEditor; - -/** - * Class types. - */ -class CtClassType extends CtClass { - ClassPool classPool; - boolean wasChanged; - private boolean wasFrozen; - boolean wasPruned; - boolean gcConstPool; // if true, the constant pool entries will be garbage collected. - ClassFile classfile; - byte[] rawClassfile; // backup storage - - private WeakReference memberCache; - private AccessorMaker accessors; - - private FieldInitLink fieldInitializers; - private Hashtable hiddenMethods; // must be synchronous - private int uniqueNumberSeed; - - private boolean doPruning = ClassPool.doPruning; - private int getCount; - private static final int GET_THRESHOLD = 2; // see compress() - - CtClassType(String name, ClassPool cp) { - super(name); - classPool = cp; - wasChanged = wasFrozen = wasPruned = gcConstPool = false; - classfile = null; - rawClassfile = null; - memberCache = null; - accessors = null; - fieldInitializers = null; - hiddenMethods = null; - uniqueNumberSeed = 0; - getCount = 0; - } - - CtClassType(InputStream ins, ClassPool cp) throws IOException { - this((String)null, cp); - classfile = new ClassFile(new DataInputStream(ins)); - qualifiedName = classfile.getName(); - } - - CtClassType(ClassFile cf, ClassPool cp) { - this((String)null, cp); - classfile = cf; - qualifiedName = classfile.getName(); - } - - protected void extendToString(StringBuffer buffer) { - if (wasChanged) - buffer.append("changed "); - - if (wasFrozen) - buffer.append("frozen "); - - if (wasPruned) - buffer.append("pruned "); - - buffer.append(Modifier.toString(getModifiers())); - buffer.append(" class "); - buffer.append(getName()); - - try { - CtClass ext = getSuperclass(); - if (ext != null) { - String name = ext.getName(); - if (!name.equals("java.lang.Object")) - buffer.append(" extends " + ext.getName()); - } - } - catch (NotFoundException e) { - buffer.append(" extends ??"); - } - - try { - CtClass[] intf = getInterfaces(); - if (intf.length > 0) - buffer.append(" implements "); - - for (int i = 0; i < intf.length; ++i) { - buffer.append(intf[i].getName()); - buffer.append(", "); - } - } - catch (NotFoundException e) { - buffer.append(" extends ??"); - } - - CtMember.Cache memCache = getMembers(); - exToString(buffer, " fields=", - memCache.fieldHead(), memCache.lastField()); - exToString(buffer, " constructors=", - memCache.consHead(), memCache.lastCons()); - exToString(buffer, " methods=", - memCache.methodHead(), memCache.lastMethod()); - } - - private void exToString(StringBuffer buffer, String msg, - CtMember head, CtMember tail) { - buffer.append(msg); - while (head != tail) { - head = head.next(); - buffer.append(head); - buffer.append(", "); - } - } - - public AccessorMaker getAccessorMaker() { - if (accessors == null) - accessors = new AccessorMaker(this); - - return accessors; - } - - public ClassFile getClassFile2() { - ClassFile cfile = classfile; - if (cfile != null) - return cfile; - - classPool.compress(); - if (rawClassfile != null) { - try { - classfile = new ClassFile(new DataInputStream( - new ByteArrayInputStream(rawClassfile))); - rawClassfile = null; - getCount = GET_THRESHOLD; - return classfile; - } - catch (IOException e) { - throw new RuntimeException(e.toString(), e); - } - } - - InputStream fin = null; - try { - fin = classPool.openClassfile(getName()); - if (fin == null) - throw new NotFoundException(getName()); - - fin = new BufferedInputStream(fin); - ClassFile cf = new ClassFile(new DataInputStream(fin)); - if (!cf.getName().equals(qualifiedName)) - throw new RuntimeException("cannot find " + qualifiedName + ": " - + cf.getName() + " found in " - + qualifiedName.replace('.', '/') + ".class"); - - classfile = cf; - return cf; - } - catch (NotFoundException e) { - throw new RuntimeException(e.toString(), e); - } - catch (IOException e) { - throw new RuntimeException(e.toString(), e); - } - finally { - if (fin != null) - try { - fin.close(); - } - catch (IOException e) {} - } - } - - /* Inherited from CtClass. Called by get() in ClassPool. - * - * @see javassist.CtClass#incGetCounter() - * @see #toBytecode(DataOutputStream) - */ - final void incGetCounter() { ++getCount; } - - /** - * Invoked from ClassPool#compress(). - * It releases the class files that have not been recently used - * if they are unmodified. - */ - void compress() { - if (getCount < GET_THRESHOLD) - if (!isModified() && ClassPool.releaseUnmodifiedClassFile) - removeClassFile(); - else if (isFrozen() && !wasPruned) - saveClassFile(); - - getCount = 0; - } - - /** - * Converts a ClassFile object into a byte array - * for saving memory space. - */ - private synchronized void saveClassFile() { - /* getMembers() and releaseClassFile() are also synchronized. - */ - if (classfile == null || hasMemberCache() != null) - return; - - ByteArrayOutputStream barray = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(barray); - try { - classfile.write(out); - barray.close(); - rawClassfile = barray.toByteArray(); - classfile = null; - } - catch (IOException e) {} - } - - private synchronized void removeClassFile() { - if (classfile != null && !isModified() && hasMemberCache() == null) - classfile = null; - } - - public ClassPool getClassPool() { return classPool; } - - void setClassPool(ClassPool cp) { classPool = cp; } - - public URL getURL() throws NotFoundException { - URL url = classPool.find(getName()); - if (url == null) - throw new NotFoundException(getName()); - else - return url; - } - - public boolean isModified() { return wasChanged; } - - public boolean isFrozen() { return wasFrozen; } - - public void freeze() { wasFrozen = true; } - - void checkModify() throws RuntimeException { - if (isFrozen()) { - String msg = getName() + " class is frozen"; - if (wasPruned) - msg += " and pruned"; - - throw new RuntimeException(msg); - } - - wasChanged = true; - } - - public void defrost() { - checkPruned("defrost"); - wasFrozen = false; - } - - public boolean subtypeOf(CtClass clazz) throws NotFoundException { - int i; - String cname = clazz.getName(); - if (this == clazz || getName().equals(cname)) - return true; - - ClassFile file = getClassFile2(); - String supername = file.getSuperclass(); - if (supername != null && supername.equals(cname)) - return true; - - String[] ifs = file.getInterfaces(); - int num = ifs.length; - for (i = 0; i < num; ++i) - if (ifs[i].equals(cname)) - return true; - - if (supername != null && classPool.get(supername).subtypeOf(clazz)) - return true; - - for (i = 0; i < num; ++i) - if (classPool.get(ifs[i]).subtypeOf(clazz)) - return true; - - return false; - } - - public void setName(String name) throws RuntimeException { - String oldname = getName(); - if (name.equals(oldname)) - return; - - // check this in advance although classNameChanged() below does. - classPool.checkNotFrozen(name); - ClassFile cf = getClassFile2(); - super.setName(name); - cf.setName(name); - nameReplaced(); - classPool.classNameChanged(oldname, this); - } - - public String getGenericSignature() { - SignatureAttribute sa - = (SignatureAttribute)getClassFile2().getAttribute(SignatureAttribute.tag); - return sa == null ? null : sa.getSignature(); - } - - public void setGenericSignature(String sig) { - ClassFile cf = getClassFile(); - SignatureAttribute sa = new SignatureAttribute(cf.getConstPool(), sig); - cf.addAttribute(sa); - } - - public void replaceClassName(ClassMap classnames) - throws RuntimeException - { - String oldClassName = getName(); - String newClassName - = (String)classnames.get(Descriptor.toJvmName(oldClassName)); - if (newClassName != null) { - newClassName = Descriptor.toJavaName(newClassName); - // check this in advance although classNameChanged() below does. - classPool.checkNotFrozen(newClassName); - } - - super.replaceClassName(classnames); - ClassFile cf = getClassFile2(); - cf.renameClass(classnames); - nameReplaced(); - - if (newClassName != null) { - super.setName(newClassName); - classPool.classNameChanged(oldClassName, this); - } - } - - public void replaceClassName(String oldname, String newname) - throws RuntimeException - { - String thisname = getName(); - if (thisname.equals(oldname)) - setName(newname); - else { - super.replaceClassName(oldname, newname); - getClassFile2().renameClass(oldname, newname); - nameReplaced(); - } - } - - public boolean isInterface() { - return Modifier.isInterface(getModifiers()); - } - - public boolean isAnnotation() { - return Modifier.isAnnotation(getModifiers()); - } - - public boolean isEnum() { - return Modifier.isEnum(getModifiers()); - } - - public int getModifiers() { - ClassFile cf = getClassFile2(); - int acc = cf.getAccessFlags(); - acc = AccessFlag.clear(acc, AccessFlag.SUPER); - int inner = cf.getInnerAccessFlags(); - if (inner != -1 && (inner & AccessFlag.STATIC) != 0) - acc |= AccessFlag.STATIC; - - return AccessFlag.toModifier(acc); - } - - public CtClass[] getNestedClasses() throws NotFoundException { - ClassFile cf = getClassFile2(); - InnerClassesAttribute ica - = (InnerClassesAttribute)cf.getAttribute(InnerClassesAttribute.tag); - if (ica == null) - return new CtClass[0]; - - String thisName = cf.getName() + "$"; - int n = ica.tableLength(); - ArrayList list = new ArrayList(n); - for (int i = 0; i < n; i++) { - String name = ica.innerClass(i); - if (name != null) - if (name.startsWith(thisName)) { - // if it is an immediate nested class - if (name.lastIndexOf('$') < thisName.length()) - list.add(classPool.get(name)); - } - } - - return (CtClass[])list.toArray(new CtClass[list.size()]); - } - - public void setModifiers(int mod) { - ClassFile cf = getClassFile2(); - if (Modifier.isStatic(mod)) { - int flags = cf.getInnerAccessFlags(); - if (flags != -1 && (flags & AccessFlag.STATIC) != 0) - mod = mod & ~Modifier.STATIC; - else - throw new RuntimeException("cannot change " + getName() + " into a static class"); - } - - checkModify(); - cf.setAccessFlags(AccessFlag.of(mod)); - } - - public boolean hasAnnotation(Class clz) { - ClassFile cf = getClassFile2(); - AnnotationsAttribute ainfo = (AnnotationsAttribute) - cf.getAttribute(AnnotationsAttribute.invisibleTag); - AnnotationsAttribute ainfo2 = (AnnotationsAttribute) - cf.getAttribute(AnnotationsAttribute.visibleTag); - return hasAnnotationType(clz, getClassPool(), ainfo, ainfo2); - } - - static boolean hasAnnotationType(Class clz, ClassPool cp, - AnnotationsAttribute a1, AnnotationsAttribute a2) - { - Annotation[] anno1, anno2; - - if (a1 == null) - anno1 = null; - else - anno1 = a1.getAnnotations(); - - if (a2 == null) - anno2 = null; - else - anno2 = a2.getAnnotations(); - - String typeName = clz.getName(); - if (anno1 != null) - for (int i = 0; i < anno1.length; i++) - if (anno1[i].getTypeName().equals(typeName)) - return true; - - if (anno2 != null) - for (int i = 0; i < anno2.length; i++) - if (anno2[i].getTypeName().equals(typeName)) - return true; - - return false; - } - - public Object getAnnotation(Class clz) throws ClassNotFoundException { - ClassFile cf = getClassFile2(); - AnnotationsAttribute ainfo = (AnnotationsAttribute) - cf.getAttribute(AnnotationsAttribute.invisibleTag); - AnnotationsAttribute ainfo2 = (AnnotationsAttribute) - cf.getAttribute(AnnotationsAttribute.visibleTag); - return getAnnotationType(clz, getClassPool(), ainfo, ainfo2); - } - - static Object getAnnotationType(Class clz, ClassPool cp, - AnnotationsAttribute a1, AnnotationsAttribute a2) - throws ClassNotFoundException - { - Annotation[] anno1, anno2; - - if (a1 == null) - anno1 = null; - else - anno1 = a1.getAnnotations(); - - if (a2 == null) - anno2 = null; - else - anno2 = a2.getAnnotations(); - - String typeName = clz.getName(); - if (anno1 != null) - for (int i = 0; i < anno1.length; i++) - if (anno1[i].getTypeName().equals(typeName)) - return toAnnoType(anno1[i], cp); - - if (anno2 != null) - for (int i = 0; i < anno2.length; i++) - if (anno2[i].getTypeName().equals(typeName)) - return toAnnoType(anno2[i], cp); - - return null; - } - - public Object[] getAnnotations() throws ClassNotFoundException { - return getAnnotations(false); - } - - public Object[] getAvailableAnnotations(){ - try { - return getAnnotations(true); - } - catch (ClassNotFoundException e) { - throw new RuntimeException("Unexpected exception ", e); - } - } - - private Object[] getAnnotations(boolean ignoreNotFound) - throws ClassNotFoundException - { - ClassFile cf = getClassFile2(); - AnnotationsAttribute ainfo = (AnnotationsAttribute) - cf.getAttribute(AnnotationsAttribute.invisibleTag); - AnnotationsAttribute ainfo2 = (AnnotationsAttribute) - cf.getAttribute(AnnotationsAttribute.visibleTag); - return toAnnotationType(ignoreNotFound, getClassPool(), ainfo, ainfo2); - } - - static Object[] toAnnotationType(boolean ignoreNotFound, ClassPool cp, - AnnotationsAttribute a1, AnnotationsAttribute a2) - throws ClassNotFoundException - { - Annotation[] anno1, anno2; - int size1, size2; - - if (a1 == null) { - anno1 = null; - size1 = 0; - } - else { - anno1 = a1.getAnnotations(); - size1 = anno1.length; - } - - if (a2 == null) { - anno2 = null; - size2 = 0; - } - else { - anno2 = a2.getAnnotations(); - size2 = anno2.length; - } - - if (!ignoreNotFound){ - Object[] result = new Object[size1 + size2]; - for (int i = 0; i < size1; i++) - result[i] = toAnnoType(anno1[i], cp); - - for (int j = 0; j < size2; j++) - result[j + size1] = toAnnoType(anno2[j], cp); - - return result; - } - else{ - ArrayList annotations = new ArrayList(); - for (int i = 0 ; i < size1 ; i++){ - try{ - annotations.add(toAnnoType(anno1[i], cp)); - } - catch(ClassNotFoundException e){} - } - for (int j = 0; j < size2; j++) { - try{ - annotations.add(toAnnoType(anno2[j], cp)); - } - catch(ClassNotFoundException e){} - } - - return annotations.toArray(); - } - } - - static Object[][] toAnnotationType(boolean ignoreNotFound, ClassPool cp, - ParameterAnnotationsAttribute a1, - ParameterAnnotationsAttribute a2, - MethodInfo minfo) - throws ClassNotFoundException - { - int numParameters = 0; - if (a1 != null) - numParameters = a1.numParameters(); - else if (a2 != null) - numParameters = a2.numParameters(); - else - numParameters = Descriptor.numOfParameters(minfo.getDescriptor()); - - Object[][] result = new Object[numParameters][]; - for (int i = 0; i < numParameters; i++) { - Annotation[] anno1, anno2; - int size1, size2; - - if (a1 == null) { - anno1 = null; - size1 = 0; - } - else { - anno1 = a1.getAnnotations()[i]; - size1 = anno1.length; - } - - if (a2 == null) { - anno2 = null; - size2 = 0; - } - else { - anno2 = a2.getAnnotations()[i]; - size2 = anno2.length; - } - - if (!ignoreNotFound){ - result[i] = new Object[size1 + size2]; - for (int j = 0; j < size1; ++j) - result[i][j] = toAnnoType(anno1[j], cp); - - for (int j = 0; j < size2; ++j) - result[i][j + size1] = toAnnoType(anno2[j], cp); - } - else{ - ArrayList annotations = new ArrayList(); - for (int j = 0 ; j < size1 ; j++){ - try{ - annotations.add(toAnnoType(anno1[j], cp)); - } - catch(ClassNotFoundException e){} - } - for (int j = 0; j < size2; j++){ - try{ - annotations.add(toAnnoType(anno2[j], cp)); - } - catch(ClassNotFoundException e){} - } - - result[i] = annotations.toArray(); - } - } - - return result; - } - - private static Object toAnnoType(Annotation anno, ClassPool cp) - throws ClassNotFoundException - { - try { - ClassLoader cl = cp.getClassLoader(); - return anno.toAnnotationType(cl, cp); - } - catch (ClassNotFoundException e) { - ClassLoader cl2 = cp.getClass().getClassLoader(); - try { - return anno.toAnnotationType(cl2, cp); - } - catch (ClassNotFoundException e2){ - try { - Class clazz = cp.get(anno.getTypeName()).toClass(); - return com.wenshuo.agent.javassist.bytecode.annotation.AnnotationImpl.make( - clazz.getClassLoader(), - clazz, cp, anno); - } - catch (Throwable e3) { - throw new ClassNotFoundException(anno.getTypeName()); - } - } - } - } - - public boolean subclassOf(CtClass superclass) { - if (superclass == null) - return false; - - String superName = superclass.getName(); - CtClass curr = this; - try { - while (curr != null) { - if (curr.getName().equals(superName)) - return true; - - curr = curr.getSuperclass(); - } - } - catch (Exception ignored) {} - return false; - } - - public CtClass getSuperclass() throws NotFoundException { - String supername = getClassFile2().getSuperclass(); - if (supername == null) - return null; - else - return classPool.get(supername); - } - - public void setSuperclass(CtClass clazz) throws CannotCompileException { - checkModify(); - if (isInterface()) - addInterface(clazz); - else - getClassFile2().setSuperclass(clazz.getName()); - } - - public CtClass[] getInterfaces() throws NotFoundException { - String[] ifs = getClassFile2().getInterfaces(); - int num = ifs.length; - CtClass[] ifc = new CtClass[num]; - for (int i = 0; i < num; ++i) - ifc[i] = classPool.get(ifs[i]); - - return ifc; - } - - public void setInterfaces(CtClass[] list) { - checkModify(); - String[] ifs; - if (list == null) - ifs = new String[0]; - else { - int num = list.length; - ifs = new String[num]; - for (int i = 0; i < num; ++i) - ifs[i] = list[i].getName(); - } - - getClassFile2().setInterfaces(ifs); - } - - public void addInterface(CtClass anInterface) { - checkModify(); - if (anInterface != null) - getClassFile2().addInterface(anInterface.getName()); - } - - public CtClass getDeclaringClass() throws NotFoundException { - ClassFile cf = getClassFile2(); - InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( - InnerClassesAttribute.tag); - if (ica == null) - return null; - - String name = getName(); - int n = ica.tableLength(); - for (int i = 0; i < n; ++i) - if (name.equals(ica.innerClass(i))) { - String outName = ica.outerClass(i); - if (outName != null) - return classPool.get(outName); - else { - // maybe anonymous or local class. - EnclosingMethodAttribute ema - = (EnclosingMethodAttribute)cf.getAttribute( - EnclosingMethodAttribute.tag); - if (ema != null) - return classPool.get(ema.className()); - } - } - - return null; - } - - public CtBehavior getEnclosingBehavior() throws NotFoundException { - ClassFile cf = getClassFile2(); - EnclosingMethodAttribute ema - = (EnclosingMethodAttribute)cf.getAttribute( - EnclosingMethodAttribute.tag); - if (ema == null) - return null; - else { - CtClass enc = classPool.get(ema.className()); - String name = ema.methodName(); - if (MethodInfo.nameInit.equals(name)) - return enc.getConstructor(ema.methodDescriptor()); - else if(MethodInfo.nameClinit.equals(name)) - return enc.getClassInitializer(); - else - return enc.getMethod(name, ema.methodDescriptor()); - } - } - - public CtClass makeNestedClass(String name, boolean isStatic) { - if (!isStatic) - throw new RuntimeException( - "sorry, only nested static class is supported"); - - checkModify(); - CtClass c = classPool.makeNestedClass(getName() + "$" + name); - ClassFile cf = getClassFile2(); - ClassFile cf2 = c.getClassFile2(); - InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( - InnerClassesAttribute.tag); - if (ica == null) { - ica = new InnerClassesAttribute(cf.getConstPool()); - cf.addAttribute(ica); - } - - ica.append(c.getName(), this.getName(), name, - (cf2.getAccessFlags() & ~AccessFlag.SUPER) | AccessFlag.STATIC); - cf2.addAttribute(ica.copy(cf2.getConstPool(), null)); - return c; - } - - /* flush cached names. - */ - private void nameReplaced() { - CtMember.Cache cache = hasMemberCache(); - if (cache != null) { - CtMember mth = cache.methodHead(); - CtMember tail = cache.lastMethod(); - while (mth != tail) { - mth = mth.next(); - mth.nameReplaced(); - } - } - } - - /** - * Returns null if members are not cached. - */ - protected CtMember.Cache hasMemberCache() { - if (memberCache != null) - return (CtMember.Cache)memberCache.get(); - else - return null; - } - - protected synchronized CtMember.Cache getMembers() { - CtMember.Cache cache = null; - if (memberCache == null - || (cache = (CtMember.Cache)memberCache.get()) == null) { - cache = new CtMember.Cache(this); - makeFieldCache(cache); - makeBehaviorCache(cache); - memberCache = new WeakReference(cache); - } - - return cache; - } - - private void makeFieldCache(CtMember.Cache cache) { - List list = getClassFile2().getFields(); - int n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - CtField newField = new CtField(finfo, this); - cache.addField(newField); - } - } - - private void makeBehaviorCache(CtMember.Cache cache) { - List list = getClassFile2().getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - if (minfo.isMethod()) { - CtMethod newMethod = new CtMethod(minfo, this); - cache.addMethod(newMethod); - } - else { - CtConstructor newCons = new CtConstructor(minfo, this); - cache.addConstructor(newCons); - } - } - } - - public CtField[] getFields() { - ArrayList alist = new ArrayList(); - getFields(alist, this); - return (CtField[])alist.toArray(new CtField[alist.size()]); - } - - private static void getFields(ArrayList alist, CtClass cc) { - int i, num; - if (cc == null) - return; - - try { - getFields(alist, cc.getSuperclass()); - } - catch (NotFoundException e) {} - - try { - CtClass[] ifs = cc.getInterfaces(); - num = ifs.length; - for (i = 0; i < num; ++i) - getFields(alist, ifs[i]); - } - catch (NotFoundException e) {} - - CtMember.Cache memCache = ((CtClassType)cc).getMembers(); - CtMember field = memCache.fieldHead(); - CtMember tail = memCache.lastField(); - while (field != tail) { - field = field.next(); - if (!Modifier.isPrivate(field.getModifiers())) - alist.add(field); - } - } - - public CtField getField(String name, String desc) throws NotFoundException { - CtField f = getField2(name, desc); - return checkGetField(f, name, desc); - } - - private CtField checkGetField(CtField f, String name, String desc) - throws NotFoundException - { - if (f == null) { - String msg = "field: " + name; - if (desc != null) - msg += " type " + desc; - - throw new NotFoundException(msg + " in " + getName()); - } - else - return f; - } - - CtField getField2(String name, String desc) { - CtField df = getDeclaredField2(name, desc); - if (df != null) - return df; - - try { - CtClass[] ifs = getInterfaces(); - int num = ifs.length; - for (int i = 0; i < num; ++i) { - CtField f = ifs[i].getField2(name, desc); - if (f != null) - return f; - } - - CtClass s = getSuperclass(); - if (s != null) - return s.getField2(name, desc); - } - catch (NotFoundException e) {} - return null; - } - - public CtField[] getDeclaredFields() { - CtMember.Cache memCache = getMembers(); - CtMember field = memCache.fieldHead(); - CtMember tail = memCache.lastField(); - int num = CtMember.Cache.count(field, tail); - CtField[] cfs = new CtField[num]; - int i = 0; - while (field != tail) { - field = field.next(); - cfs[i++] = (CtField)field; - } - - return cfs; - } - - public CtField getDeclaredField(String name) throws NotFoundException { - return getDeclaredField(name, null); - } - - public CtField getDeclaredField(String name, String desc) throws NotFoundException { - CtField f = getDeclaredField2(name, desc); - return checkGetField(f, name, desc); - } - - private CtField getDeclaredField2(String name, String desc) { - CtMember.Cache memCache = getMembers(); - CtMember field = memCache.fieldHead(); - CtMember tail = memCache.lastField(); - while (field != tail) { - field = field.next(); - if (field.getName().equals(name) - && (desc == null || desc.equals(field.getSignature()))) - return (CtField)field; - } - - return null; - } - - public CtBehavior[] getDeclaredBehaviors() { - CtMember.Cache memCache = getMembers(); - CtMember cons = memCache.consHead(); - CtMember consTail = memCache.lastCons(); - int cnum = CtMember.Cache.count(cons, consTail); - CtMember mth = memCache.methodHead(); - CtMember mthTail = memCache.lastMethod(); - int mnum = CtMember.Cache.count(mth, mthTail); - - CtBehavior[] cb = new CtBehavior[cnum + mnum]; - int i = 0; - while (cons != consTail) { - cons = cons.next(); - cb[i++] = (CtBehavior)cons; - } - - while (mth != mthTail) { - mth = mth.next(); - cb[i++] = (CtBehavior)mth; - } - - return cb; - } - - public CtConstructor[] getConstructors() { - CtMember.Cache memCache = getMembers(); - CtMember cons = memCache.consHead(); - CtMember consTail = memCache.lastCons(); - - int n = 0; - CtMember mem = cons; - while (mem != consTail) { - mem = mem.next(); - if (isPubCons((CtConstructor)mem)) - n++; - } - - CtConstructor[] result = new CtConstructor[n]; - int i = 0; - mem = cons; - while (mem != consTail) { - mem = mem.next(); - CtConstructor cc = (CtConstructor)mem; - if (isPubCons(cc)) - result[i++] = cc; - } - - return result; - } - - private static boolean isPubCons(CtConstructor cons) { - return !Modifier.isPrivate(cons.getModifiers()) - && cons.isConstructor(); - } - - public CtConstructor getConstructor(String desc) - throws NotFoundException - { - CtMember.Cache memCache = getMembers(); - CtMember cons = memCache.consHead(); - CtMember consTail = memCache.lastCons(); - - while (cons != consTail) { - cons = cons.next(); - CtConstructor cc = (CtConstructor)cons; - if (cc.getMethodInfo2().getDescriptor().equals(desc) - && cc.isConstructor()) - return cc; - } - - return super.getConstructor(desc); - } - - public CtConstructor[] getDeclaredConstructors() { - CtMember.Cache memCache = getMembers(); - CtMember cons = memCache.consHead(); - CtMember consTail = memCache.lastCons(); - - int n = 0; - CtMember mem = cons; - while (mem != consTail) { - mem = mem.next(); - CtConstructor cc = (CtConstructor)mem; - if (cc.isConstructor()) - n++; - } - - CtConstructor[] result = new CtConstructor[n]; - int i = 0; - mem = cons; - while (mem != consTail) { - mem = mem.next(); - CtConstructor cc = (CtConstructor)mem; - if (cc.isConstructor()) - result[i++] = cc; - } - - return result; - } - - public CtConstructor getClassInitializer() { - CtMember.Cache memCache = getMembers(); - CtMember cons = memCache.consHead(); - CtMember consTail = memCache.lastCons(); - - while (cons != consTail) { - cons = cons.next(); - CtConstructor cc = (CtConstructor)cons; - if (cc.isClassInitializer()) - return cc; - } - - return null; - } - - public CtMethod[] getMethods() { - HashMap h = new HashMap(); - getMethods0(h, this); - return (CtMethod[])h.values().toArray(new CtMethod[h.size()]); - } - - private static void getMethods0(HashMap h, CtClass cc) { - try { - CtClass[] ifs = cc.getInterfaces(); - int size = ifs.length; - for (int i = 0; i < size; ++i) - getMethods0(h, ifs[i]); - } - catch (NotFoundException e) {} - - try { - CtClass s = cc.getSuperclass(); - if (s != null) - getMethods0(h, s); - } - catch (NotFoundException e) {} - - if (cc instanceof CtClassType) { - CtMember.Cache memCache = ((CtClassType)cc).getMembers(); - CtMember mth = memCache.methodHead(); - CtMember mthTail = memCache.lastMethod(); - - while (mth != mthTail) { - mth = mth.next(); - if (!Modifier.isPrivate(mth.getModifiers())) - h.put(((CtMethod)mth).getStringRep(), mth); - } - } - } - - public CtMethod getMethod(String name, String desc) - throws NotFoundException - { - CtMethod m = getMethod0(this, name, desc); - if (m != null) - return m; - else - throw new NotFoundException(name + "(..) is not found in " - + getName()); - } - - private static CtMethod getMethod0(CtClass cc, - String name, String desc) { - if (cc instanceof CtClassType) { - CtMember.Cache memCache = ((CtClassType)cc).getMembers(); - CtMember mth = memCache.methodHead(); - CtMember mthTail = memCache.lastMethod(); - - while (mth != mthTail) { - mth = mth.next(); - if (mth.getName().equals(name) - && ((CtMethod)mth).getMethodInfo2().getDescriptor().equals(desc)) - return (CtMethod)mth; - } - } - - try { - CtClass s = cc.getSuperclass(); - if (s != null) { - CtMethod m = getMethod0(s, name, desc); - if (m != null) - return m; - } - } - catch (NotFoundException e) {} - - try { - CtClass[] ifs = cc.getInterfaces(); - int size = ifs.length; - for (int i = 0; i < size; ++i) { - CtMethod m = getMethod0(ifs[i], name, desc); - if (m != null) - return m; - } - } - catch (NotFoundException e) {} - return null; - } - - public CtMethod[] getDeclaredMethods() { - CtMember.Cache memCache = getMembers(); - CtMember mth = memCache.methodHead(); - CtMember mthTail = memCache.lastMethod(); - int num = CtMember.Cache.count(mth, mthTail); - CtMethod[] cms = new CtMethod[num]; - int i = 0; - while (mth != mthTail) { - mth = mth.next(); - cms[i++] = (CtMethod)mth; - } - - return cms; - } - - public CtMethod[] getDeclaredMethods(String name) throws NotFoundException { - CtMember.Cache memCache = getMembers(); - CtMember mth = memCache.methodHead(); - CtMember mthTail = memCache.lastMethod(); - ArrayList methods = new ArrayList(); - while (mth != mthTail) { - mth = mth.next(); - if (mth.getName().equals(name)) - methods.add((CtMethod)mth); - } - - return methods.toArray(new CtMethod[methods.size()]); - } - - public CtMethod getDeclaredMethod(String name) throws NotFoundException { - CtMember.Cache memCache = getMembers(); - CtMember mth = memCache.methodHead(); - CtMember mthTail = memCache.lastMethod(); - while (mth != mthTail) { - mth = mth.next(); - if (mth.getName().equals(name)) - return (CtMethod)mth; - } - - throw new NotFoundException(name + "(..) is not found in " - + getName()); - } - - public CtMethod getDeclaredMethod(String name, CtClass[] params) - throws NotFoundException - { - String desc = Descriptor.ofParameters(params); - CtMember.Cache memCache = getMembers(); - CtMember mth = memCache.methodHead(); - CtMember mthTail = memCache.lastMethod(); - - while (mth != mthTail) { - mth = mth.next(); - if (mth.getName().equals(name) - && ((CtMethod)mth).getMethodInfo2().getDescriptor().startsWith(desc)) - return (CtMethod)mth; - } - - throw new NotFoundException(name + "(..) is not found in " - + getName()); - } - - public void addField(CtField f, String init) - throws CannotCompileException - { - addField(f, CtField.Initializer.byExpr(init)); - } - - public void addField(CtField f, CtField.Initializer init) - throws CannotCompileException - { - checkModify(); - if (f.getDeclaringClass() != this) - throw new CannotCompileException("cannot add"); - - if (init == null) - init = f.getInit(); - - if (init != null) { - init.check(f.getSignature()); - int mod = f.getModifiers(); - if (Modifier.isStatic(mod) && Modifier.isFinal(mod)) - try { - ConstPool cp = getClassFile2().getConstPool(); - int index = init.getConstantValue(cp, f.getType()); - if (index != 0) { - f.getFieldInfo2().addAttribute(new ConstantAttribute(cp, index)); - init = null; - } - } - catch (NotFoundException e) {} - } - - getMembers().addField(f); - getClassFile2().addField(f.getFieldInfo2()); - - if (init != null) { - FieldInitLink fil = new FieldInitLink(f, init); - FieldInitLink link = fieldInitializers; - if (link == null) - fieldInitializers = fil; - else { - while (link.next != null) - link = link.next; - - link.next = fil; - } - } - } - - public void removeField(CtField f) throws NotFoundException { - checkModify(); - FieldInfo fi = f.getFieldInfo2(); - ClassFile cf = getClassFile2(); - if (cf.getFields().remove(fi)) { - getMembers().remove(f); - gcConstPool = true; - } - else - throw new NotFoundException(f.toString()); - } - - public CtConstructor makeClassInitializer() - throws CannotCompileException - { - CtConstructor clinit = getClassInitializer(); - if (clinit != null) - return clinit; - - checkModify(); - ClassFile cf = getClassFile2(); - Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); - modifyClassConstructor(cf, code, 0, 0); - return getClassInitializer(); - } - - public void addConstructor(CtConstructor c) - throws CannotCompileException - { - checkModify(); - if (c.getDeclaringClass() != this) - throw new CannotCompileException("cannot add"); - - getMembers().addConstructor(c); - getClassFile2().addMethod(c.getMethodInfo2()); - } - - public void removeConstructor(CtConstructor m) throws NotFoundException { - checkModify(); - MethodInfo mi = m.getMethodInfo2(); - ClassFile cf = getClassFile2(); - if (cf.getMethods().remove(mi)) { - getMembers().remove(m); - gcConstPool = true; - } - else - throw new NotFoundException(m.toString()); - } - - public void addMethod(CtMethod m) throws CannotCompileException { - checkModify(); - if (m.getDeclaringClass() != this) - throw new CannotCompileException("bad declaring class"); - - int mod = m.getModifiers(); - if ((getModifiers() & Modifier.INTERFACE) != 0) { - m.setModifiers(mod | Modifier.PUBLIC); - if ((mod & Modifier.ABSTRACT) == 0) - throw new CannotCompileException( - "an interface method must be abstract: " + m.toString()); - } - - getMembers().addMethod(m); - getClassFile2().addMethod(m.getMethodInfo2()); - if ((mod & Modifier.ABSTRACT) != 0) - setModifiers(getModifiers() | Modifier.ABSTRACT); - } - - public void removeMethod(CtMethod m) throws NotFoundException { - checkModify(); - MethodInfo mi = m.getMethodInfo2(); - ClassFile cf = getClassFile2(); - if (cf.getMethods().remove(mi)) { - getMembers().remove(m); - gcConstPool = true; - } - else - throw new NotFoundException(m.toString()); - } - - public byte[] getAttribute(String name) { - AttributeInfo ai = getClassFile2().getAttribute(name); - if (ai == null) - return null; - else - return ai.get(); - } - - public void setAttribute(String name, byte[] data) { - checkModify(); - ClassFile cf = getClassFile2(); - cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data)); - } - - public void instrument(CodeConverter converter) - throws CannotCompileException - { - checkModify(); - ClassFile cf = getClassFile2(); - ConstPool cp = cf.getConstPool(); - List list = cf.getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - converter.doit(this, minfo, cp); - } - } - - public void instrument(ExprEditor editor) - throws CannotCompileException - { - checkModify(); - ClassFile cf = getClassFile2(); - List list = cf.getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - editor.doit(this, minfo); - } - } - - /** - * @see javassist.CtClass#prune() - * @see javassist.CtClass#stopPruning(boolean) - */ - public void prune() { - if (wasPruned) - return; - - wasPruned = wasFrozen = true; - getClassFile2().prune(); - } - - public void rebuildClassFile() { gcConstPool = true; } - - public void toBytecode(DataOutputStream out) - throws CannotCompileException, IOException - { - try { - if (isModified()) { - checkPruned("toBytecode"); - ClassFile cf = getClassFile2(); - if (gcConstPool) { - cf.compact(); - gcConstPool = false; - } - - modifyClassConstructor(cf); - modifyConstructors(cf); - if (debugDump != null) - dumpClassFile(cf); - - cf.write(out); - out.flush(); - fieldInitializers = null; - if (doPruning) { - // to save memory - cf.prune(); - wasPruned = true; - } - } - else { - classPool.writeClassfile(getName(), out); - // to save memory - // classfile = null; - } - - getCount = 0; - wasFrozen = true; - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (IOException e) { - throw new CannotCompileException(e); - } - } - - private void dumpClassFile(ClassFile cf) throws IOException { - DataOutputStream dump = makeFileOutput(debugDump); - try { - cf.write(dump); - } - finally { - dump.close(); - } - } - - /* See also checkModified() - */ - private void checkPruned(String method) { - if (wasPruned) - throw new RuntimeException(method + "(): " + getName() - + " was pruned."); - } - - public boolean stopPruning(boolean stop) { - boolean prev = !doPruning; - doPruning = !stop; - return prev; - } - - private void modifyClassConstructor(ClassFile cf) - throws CannotCompileException, NotFoundException - { - if (fieldInitializers == null) - return; - - Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); - Javac jv = new Javac(code, this); - int stacksize = 0; - boolean doInit = false; - for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) { - CtField f = fi.field; - if (Modifier.isStatic(f.getModifiers())) { - doInit = true; - int s = fi.init.compileIfStatic(f.getType(), f.getName(), - code, jv); - if (stacksize < s) - stacksize = s; - } - } - - if (doInit) // need an initializer for static fileds. - modifyClassConstructor(cf, code, stacksize, 0); - } - - private void modifyClassConstructor(ClassFile cf, Bytecode code, - int stacksize, int localsize) - throws CannotCompileException - { - MethodInfo m = cf.getStaticInitializer(); - if (m == null) { - code.add(Bytecode.RETURN); - code.setMaxStack(stacksize); - code.setMaxLocals(localsize); - m = new MethodInfo(cf.getConstPool(), "", "()V"); - m.setAccessFlags(AccessFlag.STATIC); - m.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(m); - CtMember.Cache cache = hasMemberCache(); - if (cache != null) - cache.addConstructor(new CtConstructor(m, this)); - } - else { - CodeAttribute codeAttr = m.getCodeAttribute(); - if (codeAttr == null) - throw new CannotCompileException("empty "); - - try { - CodeIterator it = codeAttr.iterator(); - int pos = it.insertEx(code.get()); - it.insert(code.getExceptionTable(), pos); - int maxstack = codeAttr.getMaxStack(); - if (maxstack < stacksize) - codeAttr.setMaxStack(stacksize); - - int maxlocals = codeAttr.getMaxLocals(); - if (maxlocals < localsize) - codeAttr.setMaxLocals(localsize); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - try { - m.rebuildStackMapIf6(classPool, cf); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - private void modifyConstructors(ClassFile cf) - throws CannotCompileException, NotFoundException - { - if (fieldInitializers == null) - return; - - ConstPool cp = cf.getConstPool(); - List list = cf.getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - if (minfo.isConstructor()) { - CodeAttribute codeAttr = minfo.getCodeAttribute(); - if (codeAttr != null) - try { - Bytecode init = new Bytecode(cp, 0, - codeAttr.getMaxLocals()); - CtClass[] params - = Descriptor.getParameterTypes( - minfo.getDescriptor(), - classPool); - int stacksize = makeFieldInitializer(init, params); - insertAuxInitializer(codeAttr, init, stacksize); - minfo.rebuildStackMapIf6(classPool, cf); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - } - } - - private static void insertAuxInitializer(CodeAttribute codeAttr, - Bytecode initializer, - int stacksize) - throws BadBytecode - { - CodeIterator it = codeAttr.iterator(); - int index = it.skipSuperConstructor(); - if (index < 0) { - index = it.skipThisConstructor(); - if (index >= 0) - return; // this() is called. - - // Neither this() or super() is called. - } - - int pos = it.insertEx(initializer.get()); - it.insert(initializer.getExceptionTable(), pos); - int maxstack = codeAttr.getMaxStack(); - if (maxstack < stacksize) - codeAttr.setMaxStack(stacksize); - } - - private int makeFieldInitializer(Bytecode code, CtClass[] parameters) - throws CannotCompileException, NotFoundException - { - int stacksize = 0; - Javac jv = new Javac(code, this); - try { - jv.recordParams(parameters, false); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - - for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) { - CtField f = fi.field; - if (!Modifier.isStatic(f.getModifiers())) { - int s = fi.init.compile(f.getType(), f.getName(), code, - parameters, jv); - if (stacksize < s) - stacksize = s; - } - } - - return stacksize; - } - - // Methods used by CtNewWrappedMethod - - Hashtable getHiddenMethods() { - if (hiddenMethods == null) - hiddenMethods = new Hashtable(); - - return hiddenMethods; - } - - int getUniqueNumber() { return uniqueNumberSeed++; } - - public String makeUniqueName(String prefix) { - HashMap table = new HashMap(); - makeMemberList(table); - Set keys = table.keySet(); - String[] methods = new String[keys.size()]; - keys.toArray(methods); - - if (notFindInArray(prefix, methods)) - return prefix; - - int i = 100; - String name; - do { - if (i > 999) - throw new RuntimeException("too many unique name"); - - name = prefix + i++; - } while (!notFindInArray(name, methods)); - return name; - } - - private static boolean notFindInArray(String prefix, String[] values) { - int len = values.length; - for (int i = 0; i < len; i++) - if (values[i].startsWith(prefix)) - return false; - - return true; - } - - private void makeMemberList(HashMap table) { - int mod = getModifiers(); - if (Modifier.isAbstract(mod) || Modifier.isInterface(mod)) - try { - CtClass[] ifs = getInterfaces(); - int size = ifs.length; - for (int i = 0; i < size; i++) { - CtClass ic =ifs[i]; - if (ic != null && ic instanceof CtClassType) - ((CtClassType)ic).makeMemberList(table); - } - } - catch (NotFoundException e) {} - - try { - CtClass s = getSuperclass(); - if (s != null && s instanceof CtClassType) - ((CtClassType)s).makeMemberList(table); - } - catch (NotFoundException e) {} - - List list = getClassFile2().getMethods(); - int n = list.size(); - for (int i = 0; i < n; i++) { - MethodInfo minfo = (MethodInfo)list.get(i); - table.put(minfo.getName(), this); - } - - list = getClassFile2().getFields(); - n = list.size(); - for (int i = 0; i < n; i++) { - FieldInfo finfo = (FieldInfo)list.get(i); - table.put(finfo.getName(), this); - } - } -} - -class FieldInitLink { - FieldInitLink next; - CtField field; - CtField.Initializer init; - - FieldInitLink(CtField f, CtField.Initializer i) { - next = null; - field = f; - init = i; - } -} diff --git a/src/com/wenshuo/agent/javassist/CtConstructor.java b/src/com/wenshuo/agent/javassist/CtConstructor.java deleted file mode 100644 index 58dd659..0000000 --- a/src/com/wenshuo/agent/javassist/CtConstructor.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.Javac; -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * An instance of CtConstructor represents a constructor. - * It may represent a static constructor - * (class initializer). To distinguish a constructor and a class - * initializer, call isClassInitializer(). - * - *

See the super class CtBehavior as well since - * a number of useful methods are in CtBehavior. - * - * @see CtClass#getDeclaredConstructors() - * @see CtClass#getClassInitializer() - * @see CtNewConstructor - */ -public final class CtConstructor extends CtBehavior { - protected CtConstructor(MethodInfo minfo, CtClass declaring) { - super(declaring, minfo); - } - - /** - * Creates a constructor with no constructor body. - * The created constructor - * must be added to a class with CtClass.addConstructor(). - * - *

The created constructor does not include a constructor body, - * which must be specified with setBody(). - * - * @param declaring the class to which the created method is added. - * @param parameters a list of the parameter types - * - * @see CtClass#addConstructor(CtConstructor) - * @see CtConstructor#setBody(String) - * @see CtConstructor#setBody(CtConstructor,ClassMap) - */ - public CtConstructor(CtClass[] parameters, CtClass declaring) { - this((MethodInfo)null, declaring); - ConstPool cp = declaring.getClassFile2().getConstPool(); - String desc = Descriptor.ofConstructor(parameters); - methodInfo = new MethodInfo(cp, "", desc); - setModifiers(Modifier.PUBLIC); - } - - /** - * Creates a copy of a CtConstructor object. - * The created constructor must be - * added to a class with CtClass.addConstructor(). - * - *

All occurrences of class names in the created constructor - * are replaced with names specified by - * map if map is not null. - * - *

By default, all the occurrences of the names of the class - * declaring src and the superclass are replaced - * with the name of the class and the superclass that - * the created constructor is added to. - * This is done whichever map is null or not. - * To prevent this replacement, call ClassMap.fix() - * or put() to explicitly specify replacement. - * - *

Note: if the .class notation (for example, - * String.class) is included in an expression, the - * Javac compiler may produce a helper method. - * Since this constructor never - * copies this helper method, the programmers have the responsiblity of - * copying it. Otherwise, use Class.forName() in the - * expression. - * - * @param src the source method. - * @param declaring the class to which the created method is added. - * @param map the hashtable associating original class names - * with substituted names. - * It can be null. - * - * @see CtClass#addConstructor(CtConstructor) - * @see ClassMap#fix(String) - */ - public CtConstructor(CtConstructor src, CtClass declaring, ClassMap map) - throws CannotCompileException - { - this((MethodInfo)null, declaring); - copy(src, true, map); - } - - /** - * Returns true if this object represents a constructor. - */ - public boolean isConstructor() { - return methodInfo.isConstructor(); - } - - /** - * Returns true if this object represents a static initializer. - */ - public boolean isClassInitializer() { - return methodInfo.isStaticInitializer(); - } - - /** - * Returns the constructor name followed by parameter types - * such as javassist.CtConstructor(CtClass[],CtClass). - * - * @since 3.5 - */ - public String getLongName() { - return getDeclaringClass().getName() - + (isConstructor() ? Descriptor.toString(getSignature()) - : ("." + MethodInfo.nameClinit + "()")); - } - - /** - * Obtains the name of this constructor. - * It is the same as the simple name of the class declaring this - * constructor. If this object represents a class initializer, - * then this method returns "<clinit>". - */ - public String getName() { - if (methodInfo.isStaticInitializer()) - return MethodInfo.nameClinit; - else - return declaringClass.getSimpleName(); - } - - /** - * Returns true if the constructor (or static initializer) - * is the default one. This method returns true if the constructor - * takes some arguments but it does not perform anything except - * calling super() (the no-argument constructor of - * the super class). - */ - public boolean isEmpty() { - CodeAttribute ca = getMethodInfo2().getCodeAttribute(); - if (ca == null) - return false; // native or abstract?? - // they are not allowed, though. - - ConstPool cp = ca.getConstPool(); - CodeIterator it = ca.iterator(); - try { - int pos, desc; - int op0 = it.byteAt(it.next()); - return op0 == Opcode.RETURN // empty static initializer - || (op0 == Opcode.ALOAD_0 - && it.byteAt(pos = it.next()) == Opcode.INVOKESPECIAL - && (desc = cp.isConstructor(getSuperclassName(), - it.u16bitAt(pos + 1))) != 0 - && "()V".equals(cp.getUtf8Info(desc)) - && it.byteAt(it.next()) == Opcode.RETURN - && !it.hasNext()); - } - catch (BadBytecode e) {} - return false; - } - - private String getSuperclassName() { - ClassFile cf = declaringClass.getClassFile2(); - return cf.getSuperclass(); - } - - /** - * Returns true if this constructor calls a constructor - * of the super class. This method returns false if it - * calls another constructor of this class by this(). - */ - public boolean callsSuper() throws CannotCompileException { - CodeAttribute codeAttr = methodInfo.getCodeAttribute(); - if (codeAttr != null) { - CodeIterator it = codeAttr.iterator(); - try { - int index = it.skipSuperConstructor(); - return index >= 0; - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - return false; - } - - /** - * Sets a constructor body. - * - * @param src the source code representing the constructor body. - * It must be a single statement or block. - * If it is null, the substituted - * constructor body does nothing except calling - * super(). - */ - public void setBody(String src) throws CannotCompileException { - if (src == null) - if (isClassInitializer()) - src = ";"; - else - src = "super();"; - - super.setBody(src); - } - - /** - * Copies a constructor body from another constructor. - * - *

All occurrences of the class names in the copied body - * are replaced with the names specified by - * map if map is not null. - * - * @param src the method that the body is copied from. - * @param map the hashtable associating original class names - * with substituted names. - * It can be null. - */ - public void setBody(CtConstructor src, ClassMap map) - throws CannotCompileException - { - setBody0(src.declaringClass, src.methodInfo, - declaringClass, methodInfo, map); - } - - /** - * Inserts bytecode just after another constructor in the super class - * or this class is called. - * It does not work if this object represents a class initializer. - * - * @param src the source code representing the inserted bytecode. - * It must be a single statement or block. - */ - public void insertBeforeBody(String src) throws CannotCompileException { - CtClass cc = declaringClass; - cc.checkModify(); - if (isClassInitializer()) - throw new CannotCompileException("class initializer"); - - CodeAttribute ca = methodInfo.getCodeAttribute(); - CodeIterator iterator = ca.iterator(); - Bytecode b = new Bytecode(methodInfo.getConstPool(), - ca.getMaxStack(), ca.getMaxLocals()); - b.setStackDepth(ca.getMaxStack()); - Javac jv = new Javac(b, cc); - try { - jv.recordParams(getParameterTypes(), false); - jv.compileStmnt(src); - ca.setMaxStack(b.getMaxStack()); - ca.setMaxLocals(b.getMaxLocals()); - iterator.skipConstructor(); - int pos = iterator.insertEx(b.get()); - iterator.insert(b.getExceptionTable(), pos); - methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - /* This method is called by addCatch() in CtBehavior. - * super() and this() must not be in a try statement. - */ - int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException { - CodeIterator ci = ca.iterator(); - try { - ci.skipConstructor(); - return ci.next(); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - /** - * Makes a copy of this constructor and converts it into a method. - * The signature of the mehtod is the same as the that of this constructor. - * The return type is void. The resulting method must be - * appended to the class specified by declaring. - * If this constructor is a static initializer, the resulting method takes - * no parameter. - * - *

An occurrence of another constructor call this() - * or a super constructor call super() is - * eliminated from the resulting method. - * - *

The immediate super class of the class declaring this constructor - * must be also a super class of the class declaring the resulting method. - * If the constructor accesses a field, the class declaring the resulting method - * must also declare a field with the same name and type. - * - * @param name the name of the resulting method. - * @param declaring the class declaring the resulting method. - */ - public CtMethod toMethod(String name, CtClass declaring) - throws CannotCompileException - { - return toMethod(name, declaring, null); - } - - /** - * Makes a copy of this constructor and converts it into a method. - * The signature of the method is the same as the that of this constructor. - * The return type is void. The resulting method must be - * appended to the class specified by declaring. - * If this constructor is a static initializer, the resulting method takes - * no parameter. - * - *

An occurrence of another constructor call this() - * or a super constructor call super() is - * eliminated from the resulting method. - * - *

The immediate super class of the class declaring this constructor - * must be also a super class of the class declaring the resulting method - * (this is obviously true if the second parameter declaring is - * the same as the class declaring this constructor). - * If the constructor accesses a field, the class declaring the resulting method - * must also declare a field with the same name and type. - * - * @param name the name of the resulting method. - * @param declaring the class declaring the resulting method. - * It is normally the same as the class declaring this - * constructor. - * @param map the hash table associating original class names - * with substituted names. The original class names will be - * replaced while making a copy. - * map can be null. - */ - public CtMethod toMethod(String name, CtClass declaring, ClassMap map) - throws CannotCompileException - { - CtMethod method = new CtMethod(null, declaring); - method.copy(this, false, map); - if (isConstructor()) { - MethodInfo minfo = method.getMethodInfo2(); - CodeAttribute ca = minfo.getCodeAttribute(); - if (ca != null) { - removeConsCall(ca); - try { - methodInfo.rebuildStackMapIf6(declaring.getClassPool(), - declaring.getClassFile2()); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - } - - method.setName(name); - return method; - } - - private static void removeConsCall(CodeAttribute ca) - throws CannotCompileException - { - CodeIterator iterator = ca.iterator(); - try { - int pos = iterator.skipConstructor(); - if (pos >= 0) { - int mref = iterator.u16bitAt(pos + 1); - String desc = ca.getConstPool().getMethodrefType(mref); - int num = Descriptor.numOfParameters(desc) + 1; - if (num > 3) - pos = iterator.insertGapAt(pos, num - 3, false).position; - - iterator.writeByte(Opcode.POP, pos++); // this - iterator.writeByte(Opcode.NOP, pos); - iterator.writeByte(Opcode.NOP, pos + 1); - Descriptor.Iterator it = new Descriptor.Iterator(desc); - while (true) { - it.next(); - if (it.isParameter()) - iterator.writeByte(it.is2byte() ? Opcode.POP2 : Opcode.POP, - pos++); - else - break; - } - } - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/CtField.java b/src/com/wenshuo/agent/javassist/CtField.java deleted file mode 100644 index 7489d83..0000000 --- a/src/com/wenshuo/agent/javassist/CtField.java +++ /dev/null @@ -1,1406 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.Javac; -import com.wenshuo.agent.javassist.compiler.SymbolTable; -import com.wenshuo.agent.javassist.compiler.CompileError; -import com.wenshuo.agent.javassist.compiler.ast.ASTree; -import com.wenshuo.agent.javassist.compiler.ast.IntConst; -import com.wenshuo.agent.javassist.compiler.ast.DoubleConst; -import com.wenshuo.agent.javassist.compiler.ast.StringL; - -/** - * An instance of CtField represents a field. - * - * @see CtClass#getDeclaredFields() - */ -public class CtField extends CtMember { - static final String javaLangString = "java.lang.String"; - - protected FieldInfo fieldInfo; - - /** - * Creates a CtField object. - * The created field must be added to a class - * with CtClass.addField(). - * An initial value of the field is specified - * by a CtField.Initializer object. - * - *

If getter and setter methods are needed, - * call CtNewMethod.getter() and - * CtNewMethod.setter(). - * - * @param type field type - * @param name field name - * @param declaring the class to which the field will be added. - * - * @see CtClass#addField(CtField) - * @see CtNewMethod#getter(String,CtField) - * @see CtNewMethod#setter(String,CtField) - * @see CtField.Initializer - */ - public CtField(CtClass type, String name, CtClass declaring) - throws CannotCompileException - { - this(Descriptor.of(type), name, declaring); - } - - /** - * Creates a copy of the given field. - * The created field must be added to a class - * with CtClass.addField(). - * An initial value of the field is specified - * by a CtField.Initializer object. - * - *

If getter and setter methods are needed, - * call CtNewMethod.getter() and - * CtNewMethod.setter(). - * - * @param src the original field - * @param declaring the class to which the field will be added. - * @see CtNewMethod#getter(String,CtField) - * @see CtNewMethod#setter(String,CtField) - * @see CtField.Initializer - */ - public CtField(CtField src, CtClass declaring) - throws CannotCompileException - { - this(src.fieldInfo.getDescriptor(), src.fieldInfo.getName(), - declaring); - java.util.ListIterator iterator - = src.fieldInfo.getAttributes().listIterator(); - FieldInfo fi = fieldInfo; - fi.setAccessFlags(src.fieldInfo.getAccessFlags()); - ConstPool cp = fi.getConstPool(); - while (iterator.hasNext()) { - AttributeInfo ainfo = (AttributeInfo)iterator.next(); - fi.addAttribute(ainfo.copy(cp, null)); - } - } - - private CtField(String typeDesc, String name, CtClass clazz) - throws CannotCompileException - { - super(clazz); - ClassFile cf = clazz.getClassFile2(); - if (cf == null) - throw new CannotCompileException("bad declaring class: " - + clazz.getName()); - - fieldInfo = new FieldInfo(cf.getConstPool(), name, typeDesc); - } - - CtField(FieldInfo fi, CtClass clazz) { - super(clazz); - fieldInfo = fi; - } - - /** - * Returns a String representation of the object. - */ - public String toString() { - return getDeclaringClass().getName() + "." + getName() - + ":" + fieldInfo.getDescriptor(); - } - - protected void extendToString(StringBuffer buffer) { - buffer.append(' '); - buffer.append(getName()); - buffer.append(' '); - buffer.append(fieldInfo.getDescriptor()); - } - - /* Javac.CtFieldWithInit overrides. - */ - protected ASTree getInitAST() { return null; } - - /* Called by CtClassType.addField(). - */ - Initializer getInit() { - ASTree tree = getInitAST(); - if (tree == null) - return null; - else - return Initializer.byExpr(tree); - } - - /** - * Compiles the given source code and creates a field. - * Examples of the source code are: - * - *

-     * "public String name;"
-     * "public int k = 3;"
- * - *

Note that the source code ends with ';' - * (semicolon). - * - * @param src the source text. - * @param declaring the class to which the created field is added. - */ - public static CtField make(String src, CtClass declaring) - throws CannotCompileException - { - Javac compiler = new Javac(declaring); - try { - CtMember obj = compiler.compile(src); - if (obj instanceof CtField) - return (CtField)obj; // an instance of Javac.CtFieldWithInit - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - - throw new CannotCompileException("not a field"); - } - - /** - * Returns the FieldInfo representing the field in the class file. - */ - public FieldInfo getFieldInfo() { - declaringClass.checkModify(); - return fieldInfo; - } - - /** - * Returns the FieldInfo representing the field in the class - * file (read only). - * Normal applications do not need calling this method. Use - * getFieldInfo(). - * - *

The FieldInfo object obtained by this method - * is read only. Changes to this object might not be reflected - * on a class file generated by toBytecode(), - * toClass(), etc in CtClass. - * - *

This method is available even if the CtClass - * containing this field is frozen. However, if the class is - * frozen, the FieldInfo might be also pruned. - * - * @see #getFieldInfo() - * @see CtClass#isFrozen() - * @see CtClass#prune() - */ - public FieldInfo getFieldInfo2() { return fieldInfo; } - - /** - * Returns the class declaring the field. - */ - public CtClass getDeclaringClass() { - // this is redundant but for javadoc. - return super.getDeclaringClass(); - } - - /** - * Returns the name of the field. - */ - public String getName() { - return fieldInfo.getName(); - } - - /** - * Changes the name of the field. - */ - public void setName(String newName) { - declaringClass.checkModify(); - fieldInfo.setName(newName); - } - - /** - * Returns the encoded modifiers of the field. - * - * @see Modifier - */ - public int getModifiers() { - return AccessFlag.toModifier(fieldInfo.getAccessFlags()); - } - - /** - * Sets the encoded modifiers of the field. - * - * @see Modifier - */ - public void setModifiers(int mod) { - declaringClass.checkModify(); - fieldInfo.setAccessFlags(AccessFlag.of(mod)); - } - - /** - * Returns true if the class has the specified annotation class. - * - * @param clz the annotation class. - * @return true if the annotation is found, otherwise false. - * @since 3.11 - */ - public boolean hasAnnotation(Class clz) { - FieldInfo fi = getFieldInfo2(); - AnnotationsAttribute ainfo = (AnnotationsAttribute) - fi.getAttribute(AnnotationsAttribute.invisibleTag); - AnnotationsAttribute ainfo2 = (AnnotationsAttribute) - fi.getAttribute(AnnotationsAttribute.visibleTag); - return CtClassType.hasAnnotationType(clz, getDeclaringClass().getClassPool(), - ainfo, ainfo2); - } - - /** - * Returns the annotation if the class has the specified annotation class. - * For example, if an annotation @Author is associated - * with this field, an Author object is returned. - * The member values can be obtained by calling methods on - * the Author object. - * - * @param clz the annotation class. - * @return the annotation if found, otherwise null. - * @since 3.11 - */ - public Object getAnnotation(Class clz) throws ClassNotFoundException { - FieldInfo fi = getFieldInfo2(); - AnnotationsAttribute ainfo = (AnnotationsAttribute) - fi.getAttribute(AnnotationsAttribute.invisibleTag); - AnnotationsAttribute ainfo2 = (AnnotationsAttribute) - fi.getAttribute(AnnotationsAttribute.visibleTag); - return CtClassType.getAnnotationType(clz, getDeclaringClass().getClassPool(), - ainfo, ainfo2); - } - - /** - * Returns the annotations associated with this field. - * - * @return an array of annotation-type objects. - * @see #getAvailableAnnotations() - * @since 3.1 - */ - public Object[] getAnnotations() throws ClassNotFoundException { - return getAnnotations(false); - } - - /** - * Returns the annotations associated with this field. - * If any annotations are not on the classpath, they are not included - * in the returned array. - * - * @return an array of annotation-type objects. - * @see #getAnnotations() - * @since 3.3 - */ - public Object[] getAvailableAnnotations(){ - try { - return getAnnotations(true); - } - catch (ClassNotFoundException e) { - throw new RuntimeException("Unexpected exception", e); - } - } - - private Object[] getAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { - FieldInfo fi = getFieldInfo2(); - AnnotationsAttribute ainfo = (AnnotationsAttribute) - fi.getAttribute(AnnotationsAttribute.invisibleTag); - AnnotationsAttribute ainfo2 = (AnnotationsAttribute) - fi.getAttribute(AnnotationsAttribute.visibleTag); - return CtClassType.toAnnotationType(ignoreNotFound, getDeclaringClass().getClassPool(), - ainfo, ainfo2); - } - - /** - * Returns the character string representing the type of the field. - * The field signature is represented by a character string - * called a field descriptor, which is defined in the JVM specification. - * If two fields have the same type, - * getSignature() returns the same string. - * - *

Note that the returned string is not the type signature - * contained in the SignatureAttirbute. It is - * a descriptor. - * - * @see javassist.bytecode.Descriptor - * @see #getGenericSignature() - */ - public String getSignature() { - return fieldInfo.getDescriptor(); - } - - /** - * Returns the generic signature of the field. - * It represents a type including type variables. - * - * @see SignatureAttribute#toFieldSignature(String) - * @since 3.17 - */ - public String getGenericSignature() { - SignatureAttribute sa - = (SignatureAttribute)fieldInfo.getAttribute(SignatureAttribute.tag); - return sa == null ? null : sa.getSignature(); - } - - /** - * Set the generic signature of the field. - * It represents a type including type variables. - * See {@link javassist.CtClass#setGenericSignature(String)} - * for a code sample. - * - * @param sig a new generic signature. - * @see javassist.bytecode.SignatureAttribute.ObjectType#encode() - * @since 3.17 - */ - public void setGenericSignature(String sig) { - declaringClass.checkModify(); - fieldInfo.addAttribute(new SignatureAttribute(fieldInfo.getConstPool(), sig)); - } - - /** - * Returns the type of the field. - */ - public CtClass getType() throws NotFoundException { - return Descriptor.toCtClass(fieldInfo.getDescriptor(), - declaringClass.getClassPool()); - } - - /** - * Sets the type of the field. - */ - public void setType(CtClass clazz) { - declaringClass.checkModify(); - fieldInfo.setDescriptor(Descriptor.of(clazz)); - } - - /** - * Returns the value of this field if it is a constant field. - * This method works only if the field type is a primitive type - * or String type. Otherwise, it returns null. - * A constant field is static and final. - * - * @return a Integer, Long, Float, - * Double, Boolean, - * or String object - * representing the constant value. - * null if it is not a constant field - * or if the field type is not a primitive type - * or String. - */ - public Object getConstantValue() { - // When this method is modified, - // see also getConstantFieldValue() in TypeChecker. - - int index = fieldInfo.getConstantValue(); - if (index == 0) - return null; - - ConstPool cp = fieldInfo.getConstPool(); - switch (cp.getTag(index)) { - case ConstPool.CONST_Long : - return new Long(cp.getLongInfo(index)); - case ConstPool.CONST_Float : - return new Float(cp.getFloatInfo(index)); - case ConstPool.CONST_Double : - return new Double(cp.getDoubleInfo(index)); - case ConstPool.CONST_Integer : - int value = cp.getIntegerInfo(index); - // "Z" means boolean type. - if ("Z".equals(fieldInfo.getDescriptor())) - return new Boolean(value != 0); - else - return new Integer(value); - case ConstPool.CONST_String : - return cp.getStringInfo(index); - default : - throw new RuntimeException("bad tag: " + cp.getTag(index) - + " at " + index); - } - } - - /** - * Obtains an attribute with the given name. - * If that attribute is not found in the class file, this - * method returns null. - * - *

Note that an attribute is a data block specified by - * the class file format. - * See {@link javassist.bytecode.AttributeInfo}. - * - * @param name attribute name - */ - public byte[] getAttribute(String name) { - AttributeInfo ai = fieldInfo.getAttribute(name); - if (ai == null) - return null; - else - return ai.get(); - } - - /** - * Adds an attribute. The attribute is saved in the class file. - * - *

Note that an attribute is a data block specified by - * the class file format. - * See {@link javassist.bytecode.AttributeInfo}. - * - * @param name attribute name - * @param data attribute value - */ - public void setAttribute(String name, byte[] data) { - declaringClass.checkModify(); - fieldInfo.addAttribute(new AttributeInfo(fieldInfo.getConstPool(), - name, data)); - } - - // inner classes - - /** - * Instances of this class specify how to initialize a field. - * Initializer is passed to - * CtClass.addField() with a CtField. - * - *

This class cannot be instantiated with the new operator. - * Factory methods such as byParameter() and - * byNew - * must be used for the instantiation. They create a new instance with - * the given parameters and return it. - * - * @see CtClass#addField(CtField,CtField.Initializer) - */ - public static abstract class Initializer { - /** - * Makes an initializer that assigns a constant integer value. - * The field must be integer, short, char, or byte type. - */ - public static Initializer constant(int i) { - return new IntInitializer(i); - } - - /** - * Makes an initializer that assigns a constant boolean value. - * The field must be boolean type. - */ - public static Initializer constant(boolean b) { - return new IntInitializer(b ? 1 : 0); - } - - /** - * Makes an initializer that assigns a constant long value. - * The field must be long type. - */ - public static Initializer constant(long l) { - return new LongInitializer(l); - } - - /** - * Makes an initializer that assigns a constant float value. - * The field must be float type. - */ - public static Initializer constant(float l) { - return new FloatInitializer(l); - } - - /** - * Makes an initializer that assigns a constant double value. - * The field must be double type. - */ - public static Initializer constant(double d) { - return new DoubleInitializer(d); - } - - /** - * Makes an initializer that assigns a constant string value. - * The field must be java.lang.String type. - */ - public static Initializer constant(String s) { - return new StringInitializer(s); - } - - /** - * Makes an initializer using a constructor parameter. - * - *

The initial value is the - * N-th parameter given to the constructor of the object including - * the field. If the constructor takes less than N parameters, - * the field is not initialized. - * If the field is static, it is never initialized. - * - * @param nth the n-th (>= 0) parameter is used as - * the initial value. - * If nth is 0, then the first parameter is - * used. - */ - public static Initializer byParameter(int nth) { - ParamInitializer i = new ParamInitializer(); - i.nthParam = nth; - return i; - } - - /** - * Makes an initializer creating a new object. - * - *

This initializer creates a new object and uses it as the initial - * value of the field. The constructor of the created object receives - * the parameter: - * - *

Object obj - the object including the field. - * - *

If the initialized field is static, then the constructor does - * not receive any parameters. - * - * @param objectType the class instantiated for the initial value. - */ - public static Initializer byNew(CtClass objectType) { - NewInitializer i = new NewInitializer(); - i.objectType = objectType; - i.stringParams = null; - i.withConstructorParams = false; - return i; - } - - /** - * Makes an initializer creating a new object. - * - *

This initializer creates a new object and uses it as the initial - * value of the field. The constructor of the created object receives - * the parameters: - * - *

Object obj - the object including the field.
- * String[] strs - the character strings specified - * by stringParams
- * - *

If the initialized field is static, then the constructor - * receives only strs. - * - * @param objectType the class instantiated for the initial value. - * @param stringParams the array of strings passed to the - * constructor. - */ - public static Initializer byNew(CtClass objectType, - String[] stringParams) { - NewInitializer i = new NewInitializer(); - i.objectType = objectType; - i.stringParams = stringParams; - i.withConstructorParams = false; - return i; - } - - /** - * Makes an initializer creating a new object. - * - *

This initializer creates a new object and uses it as the initial - * value of the field. The constructor of the created object receives - * the parameters: - * - *

Object obj - the object including the field.
- * Object[] args - the parameters passed to the - * constructor of the object including the - * filed. - * - *

If the initialized field is static, then the constructor does - * not receive any parameters. - * - * @param objectType the class instantiated for the initial value. - * - * @see javassist.CtField.Initializer#byNewArray(CtClass,int) - * @see javassist.CtField.Initializer#byNewArray(CtClass,int[]) - */ - public static Initializer byNewWithParams(CtClass objectType) { - NewInitializer i = new NewInitializer(); - i.objectType = objectType; - i.stringParams = null; - i.withConstructorParams = true; - return i; - } - - /** - * Makes an initializer creating a new object. - * - *

This initializer creates a new object and uses it as the initial - * value of the field. The constructor of the created object receives - * the parameters: - * - *

Object obj - the object including the field.
- * String[] strs - the character strings specified - * by stringParams
- * Object[] args - the parameters passed to the - * constructor of the object including the - * filed. - * - *

If the initialized field is static, then the constructor receives - * only strs. - * - * @param objectType the class instantiated for the initial value. - * @param stringParams the array of strings passed to the - * constructor. - */ - public static Initializer byNewWithParams(CtClass objectType, - String[] stringParams) { - NewInitializer i = new NewInitializer(); - i.objectType = objectType; - i.stringParams = stringParams; - i.withConstructorParams = true; - return i; - } - - /** - * Makes an initializer calling a static method. - * - *

This initializer calls a static method and uses the returned - * value as the initial value of the field. - * The called method receives the parameters: - * - *

Object obj - the object including the field. - * - *

If the initialized field is static, then the method does - * not receive any parameters. - * - *

The type of the returned value must be the same as the field - * type. - * - * @param methodClass the class that the static method is - * declared in. - * @param methodName the name of the satic method. - */ - public static Initializer byCall(CtClass methodClass, - String methodName) { - MethodInitializer i = new MethodInitializer(); - i.objectType = methodClass; - i.methodName = methodName; - i.stringParams = null; - i.withConstructorParams = false; - return i; - } - - /** - * Makes an initializer calling a static method. - * - *

This initializer calls a static method and uses the returned - * value as the initial value of the field. The called method - * receives the parameters: - * - *

Object obj - the object including the field.
- * String[] strs - the character strings specified - * by stringParams
- * - *

If the initialized field is static, then the method - * receive only strs. - * - *

The type of the returned value must be the same as the field - * type. - * - * @param methodClass the class that the static method is - * declared in. - * @param methodName the name of the satic method. - * @param stringParams the array of strings passed to the - * static method. - */ - public static Initializer byCall(CtClass methodClass, - String methodName, - String[] stringParams) { - MethodInitializer i = new MethodInitializer(); - i.objectType = methodClass; - i.methodName = methodName; - i.stringParams = stringParams; - i.withConstructorParams = false; - return i; - } - - /** - * Makes an initializer calling a static method. - * - *

This initializer calls a static method and uses the returned - * value as the initial value of the field. The called method - * receives the parameters: - * - *

Object obj - the object including the field.
- * Object[] args - the parameters passed to the - * constructor of the object including the - * filed. - * - *

If the initialized field is static, then the method does - * not receive any parameters. - * - *

The type of the returned value must be the same as the field - * type. - * - * @param methodClass the class that the static method is - * declared in. - * @param methodName the name of the satic method. - */ - public static Initializer byCallWithParams(CtClass methodClass, - String methodName) { - MethodInitializer i = new MethodInitializer(); - i.objectType = methodClass; - i.methodName = methodName; - i.stringParams = null; - i.withConstructorParams = true; - return i; - } - - /** - * Makes an initializer calling a static method. - * - *

This initializer calls a static method and uses the returned - * value as the initial value of the field. The called method - * receives the parameters: - * - *

Object obj - the object including the field.
- * String[] strs - the character strings specified - * by stringParams
- * Object[] args - the parameters passed to the - * constructor of the object including the - * filed. - * - *

If the initialized field is static, then the method - * receive only strs. - * - *

The type of the returned value must be the same as the field - * type. - * - * @param methodClass the class that the static method is - * declared in. - * @param methodName the name of the satic method. - * @param stringParams the array of strings passed to the - * static method. - */ - public static Initializer byCallWithParams(CtClass methodClass, - String methodName, String[] stringParams) { - MethodInitializer i = new MethodInitializer(); - i.objectType = methodClass; - i.methodName = methodName; - i.stringParams = stringParams; - i.withConstructorParams = true; - return i; - } - - /** - * Makes an initializer creating a new array. - * - * @param type the type of the array. - * @param size the size of the array. - * @throws NotFoundException if the type of the array components - * is not found. - */ - public static Initializer byNewArray(CtClass type, int size) - throws NotFoundException - { - return new ArrayInitializer(type.getComponentType(), size); - } - - /** - * Makes an initializer creating a new multi-dimensional array. - * - * @param type the type of the array. - * @param sizes an int array of the size in every - * dimension. - * The first element is the size in the first - * dimension. The second is in the second, etc. - */ - public static Initializer byNewArray(CtClass type, int[] sizes) { - return new MultiArrayInitializer(type, sizes); - } - - /** - * Makes an initializer. - * - * @param source initializer expression. - */ - public static Initializer byExpr(String source) { - return new CodeInitializer(source); - } - - static Initializer byExpr(ASTree source) { - return new PtreeInitializer(source); - } - - // Check whether this initializer is valid for the field type. - // If it is invaild, this method throws an exception. - void check(String desc) throws CannotCompileException {} - - // produce codes for initialization - abstract int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException; - - // produce codes for initialization - abstract int compileIfStatic(CtClass type, String name, - Bytecode code, Javac drv) throws CannotCompileException; - - // returns the index of CONSTANT_Integer_info etc - // if the value is constant. Otherwise, 0. - int getConstantValue(ConstPool cp, CtClass type) { return 0; } - } - - static abstract class CodeInitializer0 extends Initializer { - abstract void compileExpr(Javac drv) throws CompileError; - - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - try { - code.addAload(0); - compileExpr(drv); - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return code.getMaxStack(); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - } - - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - try { - compileExpr(drv); - code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); - return code.getMaxStack(); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - } - - int getConstantValue2(ConstPool cp, CtClass type, ASTree tree) { - if (type.isPrimitive()) { - if (tree instanceof IntConst) { - long value = ((IntConst)tree).get(); - if (type == CtClass.doubleType) - return cp.addDoubleInfo((double)value); - else if (type == CtClass.floatType) - return cp.addFloatInfo((float)value); - else if (type == CtClass.longType) - return cp.addLongInfo(value); - else if (type != CtClass.voidType) - return cp.addIntegerInfo((int)value); - } - else if (tree instanceof DoubleConst) { - double value = ((DoubleConst)tree).get(); - if (type == CtClass.floatType) - return cp.addFloatInfo((float)value); - else if (type == CtClass.doubleType) - return cp.addDoubleInfo(value); - } - } - else if (tree instanceof StringL - && type.getName().equals(javaLangString)) - return cp.addStringInfo(((StringL)tree).get()); - - return 0; - } - } - - static class CodeInitializer extends CodeInitializer0 { - private String expression; - - CodeInitializer(String expr) { expression = expr; } - - void compileExpr(Javac drv) throws CompileError { - drv.compileExpr(expression); - } - - int getConstantValue(ConstPool cp, CtClass type) { - try { - ASTree t = Javac.parseExpr(expression, new SymbolTable()); - return getConstantValue2(cp, type, t); - } - catch (CompileError e) { - return 0; - } - } - } - - static class PtreeInitializer extends CodeInitializer0 { - private ASTree expression; - - PtreeInitializer(ASTree expr) { expression = expr; } - - void compileExpr(Javac drv) throws CompileError { - drv.compileExpr(expression); - } - - int getConstantValue(ConstPool cp, CtClass type) { - return getConstantValue2(cp, type, expression); - } - } - - /** - * A field initialized with a parameter passed to the constructor - * of the class containing that field. - */ - static class ParamInitializer extends Initializer { - int nthParam; - - ParamInitializer() {} - - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - if (parameters != null && nthParam < parameters.length) { - code.addAload(0); - int nth = nthParamToLocal(nthParam, parameters, false); - int s = code.addLoad(nth, type) + 1; - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return s; // stack size - } - else - return 0; // do not initialize - } - - /** - * Computes the index of the local variable that the n-th parameter - * is assigned to. - * - * @param nth n-th parameter - * @param params list of parameter types - * @param isStatic true if the method is static. - */ - static int nthParamToLocal(int nth, CtClass[] params, - boolean isStatic) { - CtClass longType = CtClass.longType; - CtClass doubleType = CtClass.doubleType; - int k; - if (isStatic) - k = 0; - else - k = 1; // 0 is THIS. - - for (int i = 0; i < nth; ++i) { - CtClass type = params[i]; - if (type == longType || type == doubleType) - k += 2; - else - ++k; - } - - return k; - } - - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - return 0; - } - } - - /** - * A field initialized with an object created by the new operator. - */ - static class NewInitializer extends Initializer { - CtClass objectType; - String[] stringParams; - boolean withConstructorParams; - - NewInitializer() {} - - /** - * Produces codes in which a new object is created and assigned to - * the field as the initial value. - */ - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - int stacksize; - - code.addAload(0); - code.addNew(objectType); - code.add(Bytecode.DUP); - code.addAload(0); - - if (stringParams == null) - stacksize = 4; - else - stacksize = compileStringParameter(code) + 4; - - if (withConstructorParams) - stacksize += CtNewWrappedMethod.compileParameterList(code, - parameters, 1); - - code.addInvokespecial(objectType, "", getDescriptor()); - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return stacksize; - } - - private String getDescriptor() { - final String desc3 - = "(Ljava/lang/Object;[Ljava/lang/String;[Ljava/lang/Object;)V"; - - if (stringParams == null) - if (withConstructorParams) - return "(Ljava/lang/Object;[Ljava/lang/Object;)V"; - else - return "(Ljava/lang/Object;)V"; - else - if (withConstructorParams) - return desc3; - else - return "(Ljava/lang/Object;[Ljava/lang/String;)V"; - } - - /** - * Produces codes for a static field. - */ - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - String desc; - - code.addNew(objectType); - code.add(Bytecode.DUP); - - int stacksize = 2; - if (stringParams == null) - desc = "()V"; - else { - desc = "([Ljava/lang/String;)V"; - stacksize += compileStringParameter(code); - } - - code.addInvokespecial(objectType, "", desc); - code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); - return stacksize; - } - - protected final int compileStringParameter(Bytecode code) - throws CannotCompileException - { - int nparam = stringParams.length; - code.addIconst(nparam); - code.addAnewarray(javaLangString); - for (int j = 0; j < nparam; ++j) { - code.add(Bytecode.DUP); // dup - code.addIconst(j); // iconst_ - code.addLdc(stringParams[j]); // ldc ... - code.add(Bytecode.AASTORE); // aastore - } - - return 4; - } - - } - - /** - * A field initialized with the result of a static method call. - */ - static class MethodInitializer extends NewInitializer { - String methodName; - // the method class is specified by objectType. - - MethodInitializer() {} - - /** - * Produces codes in which a new object is created and assigned to - * the field as the initial value. - */ - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - int stacksize; - - code.addAload(0); - code.addAload(0); - - if (stringParams == null) - stacksize = 2; - else - stacksize = compileStringParameter(code) + 2; - - if (withConstructorParams) - stacksize += CtNewWrappedMethod.compileParameterList(code, - parameters, 1); - - String typeDesc = Descriptor.of(type); - String mDesc = getDescriptor() + typeDesc; - code.addInvokestatic(objectType, methodName, mDesc); - code.addPutfield(Bytecode.THIS, name, typeDesc); - return stacksize; - } - - private String getDescriptor() { - final String desc3 - = "(Ljava/lang/Object;[Ljava/lang/String;[Ljava/lang/Object;)"; - - if (stringParams == null) - if (withConstructorParams) - return "(Ljava/lang/Object;[Ljava/lang/Object;)"; - else - return "(Ljava/lang/Object;)"; - else - if (withConstructorParams) - return desc3; - else - return "(Ljava/lang/Object;[Ljava/lang/String;)"; - } - - /** - * Produces codes for a static field. - */ - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - String desc; - - int stacksize = 1; - if (stringParams == null) - desc = "()"; - else { - desc = "([Ljava/lang/String;)"; - stacksize += compileStringParameter(code); - } - - String typeDesc = Descriptor.of(type); - code.addInvokestatic(objectType, methodName, desc + typeDesc); - code.addPutstatic(Bytecode.THIS, name, typeDesc); - return stacksize; - } - } - - static class IntInitializer extends Initializer { - int value; - - IntInitializer(int v) { value = v; } - - void check(String desc) throws CannotCompileException { - char c = desc.charAt(0); - if (c != 'I' && c != 'S' && c != 'B' && c != 'C' && c != 'Z') - throw new CannotCompileException("type mismatch"); - } - - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - code.addAload(0); - code.addIconst(value); - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return 2; // stack size - } - - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - code.addIconst(value); - code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); - return 1; // stack size - } - - int getConstantValue(ConstPool cp, CtClass type) { - return cp.addIntegerInfo(value); - } - } - - static class LongInitializer extends Initializer { - long value; - - LongInitializer(long v) { value = v; } - - void check(String desc) throws CannotCompileException { - if (!desc.equals("J")) - throw new CannotCompileException("type mismatch"); - } - - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - code.addAload(0); - code.addLdc2w(value); - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return 3; // stack size - } - - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - code.addLdc2w(value); - code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); - return 2; // stack size - } - - int getConstantValue(ConstPool cp, CtClass type) { - if (type == CtClass.longType) - return cp.addLongInfo(value); - else - return 0; - } - } - - static class FloatInitializer extends Initializer { - float value; - - FloatInitializer(float v) { value = v; } - - void check(String desc) throws CannotCompileException { - if (!desc.equals("F")) - throw new CannotCompileException("type mismatch"); - } - - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - code.addAload(0); - code.addFconst(value); - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return 3; // stack size - } - - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - code.addFconst(value); - code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); - return 2; // stack size - } - - int getConstantValue(ConstPool cp, CtClass type) { - if (type == CtClass.floatType) - return cp.addFloatInfo(value); - else - return 0; - } - } - - static class DoubleInitializer extends Initializer { - double value; - - DoubleInitializer(double v) { value = v; } - - void check(String desc) throws CannotCompileException { - if (!desc.equals("D")) - throw new CannotCompileException("type mismatch"); - } - - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - code.addAload(0); - code.addLdc2w(value); - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return 3; // stack size - } - - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - code.addLdc2w(value); - code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); - return 2; // stack size - } - - int getConstantValue(ConstPool cp, CtClass type) { - if (type == CtClass.doubleType) - return cp.addDoubleInfo(value); - else - return 0; - } - } - - static class StringInitializer extends Initializer { - String value; - - StringInitializer(String v) { value = v; } - - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - code.addAload(0); - code.addLdc(value); - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return 2; // stack size - } - - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - code.addLdc(value); - code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); - return 1; // stack size - } - - int getConstantValue(ConstPool cp, CtClass type) { - if (type.getName().equals(javaLangString)) - return cp.addStringInfo(value); - else - return 0; - } - } - - static class ArrayInitializer extends Initializer { - CtClass type; - int size; - - ArrayInitializer(CtClass t, int s) { type = t; size = s; } - - private void addNewarray(Bytecode code) { - if (type.isPrimitive()) - code.addNewarray(((CtPrimitiveType)type).getArrayType(), - size); - else - code.addAnewarray(type, size); - } - - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - code.addAload(0); - addNewarray(code); - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return 2; // stack size - } - - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - addNewarray(code); - code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); - return 1; // stack size - } - } - - static class MultiArrayInitializer extends Initializer { - CtClass type; - int[] dim; - - MultiArrayInitializer(CtClass t, int[] d) { type = t; dim = d; } - - void check(String desc) throws CannotCompileException { - if (desc.charAt(0) != '[') - throw new CannotCompileException("type mismatch"); - } - - int compile(CtClass type, String name, Bytecode code, - CtClass[] parameters, Javac drv) - throws CannotCompileException - { - code.addAload(0); - int s = code.addMultiNewarray(type, dim); - code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); - return s + 1; // stack size - } - - int compileIfStatic(CtClass type, String name, Bytecode code, - Javac drv) throws CannotCompileException - { - int s = code.addMultiNewarray(type, dim); - code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); - return s; // stack size - } - } -} diff --git a/src/com/wenshuo/agent/javassist/CtMember.java b/src/com/wenshuo/agent/javassist/CtMember.java deleted file mode 100644 index 2e83f97..0000000 --- a/src/com/wenshuo/agent/javassist/CtMember.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -/** - * An instance of CtMember represents a field, a constructor, - * or a method. - */ -public abstract class CtMember { - CtMember next; // for internal use - protected CtClass declaringClass; - - /* Make a circular link of CtMembers declared in the - * same class so that they are garbage-collected together - * at the same time. - */ - static class Cache extends CtMember { - protected void extendToString(StringBuffer buffer) {} - public boolean hasAnnotation(Class clz) { return false; } - public Object getAnnotation(Class clz) - throws ClassNotFoundException { return null; } - public Object[] getAnnotations() - throws ClassNotFoundException { return null; } - public byte[] getAttribute(String name) { return null; } - public Object[] getAvailableAnnotations() { return null; } - public int getModifiers() { return 0; } - public String getName() { return null; } - public String getSignature() { return null; } - public void setAttribute(String name, byte[] data) {} - public void setModifiers(int mod) {} - public String getGenericSignature() { return null; } - public void setGenericSignature(String sig) {} - - private CtMember methodTail; - private CtMember consTail; // constructor tail - private CtMember fieldTail; - - Cache(CtClassType decl) { - super(decl); - methodTail = this; - consTail = this; - fieldTail = this; - fieldTail.next = this; - } - - CtMember methodHead() { return this; } - CtMember lastMethod() { return methodTail; } - CtMember consHead() { return methodTail; } // may include a static initializer - CtMember lastCons() { return consTail; } - CtMember fieldHead() { return consTail; } - CtMember lastField() { return fieldTail; } - - void addMethod(CtMember method) { - method.next = methodTail.next; - methodTail.next = method; - if (methodTail == consTail) { - consTail = method; - if (methodTail == fieldTail) - fieldTail = method; - } - - methodTail = method; - } - - /* Both constructors and a class initializer. - */ - void addConstructor(CtMember cons) { - cons.next = consTail.next; - consTail.next = cons; - if (consTail == fieldTail) - fieldTail = cons; - - consTail = cons; - } - - void addField(CtMember field) { - field.next = this; // or fieldTail.next - fieldTail.next = field; - fieldTail = field; - } - - static int count(CtMember head, CtMember tail) { - int n = 0; - while (head != tail) { - n++; - head = head.next; - } - - return n; - } - - void remove(CtMember mem) { - CtMember m = this; - CtMember node; - while ((node = m.next) != this) { - if (node == mem) { - m.next = node.next; - if (node == methodTail) - methodTail = m; - - if (node == consTail) - consTail = m; - - if (node == fieldTail) - fieldTail = m; - - break; - } - else - m = m.next; - } - } - } - - protected CtMember(CtClass clazz) { - declaringClass = clazz; - next = null; - } - - final CtMember next() { return next; } - - /** - * This method is invoked when setName() or replaceClassName() - * in CtClass is called. - * - * @see CtMethod#nameReplaced() - */ - void nameReplaced() {} - - public String toString() { - StringBuffer buffer = new StringBuffer(getClass().getName()); - buffer.append("@"); - buffer.append(Integer.toHexString(hashCode())); - buffer.append("["); - buffer.append(Modifier.toString(getModifiers())); - extendToString(buffer); - buffer.append("]"); - return buffer.toString(); - } - - /** - * Invoked by {@link #toString()} to add to the buffer and provide the - * complete value. Subclasses should invoke this method, adding a - * space before each token. The modifiers for the member are - * provided first; subclasses should provide additional data such - * as return type, field or method name, etc. - */ - protected abstract void extendToString(StringBuffer buffer); - - /** - * Returns the class that declares this member. - */ - public CtClass getDeclaringClass() { return declaringClass; } - - /** - * Returns true if this member is accessible from the given class. - */ - public boolean visibleFrom(CtClass clazz) { - int mod = getModifiers(); - if (Modifier.isPublic(mod)) - return true; - else if (Modifier.isPrivate(mod)) - return clazz == declaringClass; - else { // package or protected - String declName = declaringClass.getPackageName(); - String fromName = clazz.getPackageName(); - boolean visible; - if (declName == null) - visible = fromName == null; - else - visible = declName.equals(fromName); - - if (!visible && Modifier.isProtected(mod)) - return clazz.subclassOf(declaringClass); - - return visible; - } - } - - /** - * Obtains the modifiers of the member. - * - * @return modifiers encoded with - * javassist.Modifier. - * @see Modifier - */ - public abstract int getModifiers(); - - /** - * Sets the encoded modifiers of the member. - * - * @see Modifier - */ - public abstract void setModifiers(int mod); - - /** - * Returns true if the class has the specified annotation class. - * - * @param clz the annotation class. - * @return true if the annotation is found, otherwise false. - * @since 3.11 - */ - public abstract boolean hasAnnotation(Class clz); - - /** - * Returns the annotation if the class has the specified annotation class. - * For example, if an annotation @Author is associated - * with this member, an Author object is returned. - * The member values can be obtained by calling methods on - * the Author object. - * - * @param clz the annotation class. - * @return the annotation if found, otherwise null. - * @since 3.11 - */ - public abstract Object getAnnotation(Class clz) throws ClassNotFoundException; - - /** - * Returns the annotations associated with this member. - * For example, if an annotation @Author is associated - * with this member, the returned array contains an Author - * object. The member values can be obtained by calling methods on - * the Author object. - * - * @return an array of annotation-type objects. - * @see CtClass#getAnnotations() - */ - public abstract Object[] getAnnotations() throws ClassNotFoundException; - - /** - * Returns the annotations associated with this member. - * This method is equivalent to getAnnotations() - * except that, if any annotations are not on the classpath, - * they are not included in the returned array. - * - * @return an array of annotation-type objects. - * @see #getAnnotations() - * @see CtClass#getAvailableAnnotations() - * @since 3.3 - */ - public abstract Object[] getAvailableAnnotations(); - - /** - * Obtains the name of the member. - * - *

As for constructor names, see getName() - * in CtConstructor. - * - * @see CtConstructor#getName() - */ - public abstract String getName(); - - /** - * Returns the character string representing the signature of the member. - * If two members have the same signature (parameter types etc.), - * getSignature() returns the same string. - */ - public abstract String getSignature(); - - /** - * Returns the generic signature of the member. - * - * @see javassist.bytecode.SignatureAttribute#toFieldSignature(String) - * @see javassist.bytecode.SignatureAttribute#toMethodSignature(String) - * @see CtClass#getGenericSignature() - * @since 3.17 - */ - public abstract String getGenericSignature(); - - /** - * Sets the generic signature of the member. - * - * @param sig a new generic signature. - * @see javassist.bytecode.SignatureAttribute.ObjectType#encode() - * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode() - * @see CtClass#setGenericSignature(String) - * @since 3.17 - */ - public abstract void setGenericSignature(String sig); - - /** - * Obtains a user-defined attribute with the given name. - * If that attribute is not found in the class file, this - * method returns null. - * - *

Note that an attribute is a data block specified by - * the class file format. - * See {@link javassist.bytecode.AttributeInfo}. - * - * @param name attribute name - */ - public abstract byte[] getAttribute(String name); - - /** - * Adds a user-defined attribute. The attribute is saved in the class file. - * - *

Note that an attribute is a data block specified by - * the class file format. - * See {@link javassist.bytecode.AttributeInfo}. - * - * @param name attribute name - * @param data attribute value - */ - public abstract void setAttribute(String name, byte[] data); -} diff --git a/src/com/wenshuo/agent/javassist/CtMethod.java b/src/com/wenshuo/agent/javassist/CtMethod.java deleted file mode 100644 index 42d9f22..0000000 --- a/src/com/wenshuo/agent/javassist/CtMethod.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.*; - -/** - * An instance of CtMethod represents a method. - * - *

See the super class CtBehavior since - * a number of useful methods are in CtBehavior. - * A number of useful factory methods are in CtNewMethod. - * - * @see CtClass#getDeclaredMethods() - * @see CtNewMethod - */ -public final class CtMethod extends CtBehavior { - protected String cachedStringRep; - - /** - * @see #make(MethodInfo minfo, CtClass declaring) - */ - CtMethod(MethodInfo minfo, CtClass declaring) { - super(declaring, minfo); - cachedStringRep = null; - } - - /** - * Creates a public abstract method. The created method must be - * added to a class with CtClass.addMethod(). - * - * @param declaring the class to which the created method is added. - * @param returnType the type of the returned value - * @param mname the method name - * @param parameters a list of the parameter types - * - * @see CtClass#addMethod(CtMethod) - */ - public CtMethod(CtClass returnType, String mname, - CtClass[] parameters, CtClass declaring) { - this(null, declaring); - ConstPool cp = declaring.getClassFile2().getConstPool(); - String desc = Descriptor.ofMethod(returnType, parameters); - methodInfo = new MethodInfo(cp, mname, desc); - setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT); - } - - /** - * Creates a copy of a CtMethod object. - * The created method must be - * added to a class with CtClass.addMethod(). - * - *

All occurrences of class names in the created method - * are replaced with names specified by - * map if map is not null. - * - *

For example, suppose that a method at() is as - * follows: - * - *

-     * public X at(int i) {
-     *     return (X)super.elementAt(i);
-     * }
- * - *

(X is a class name.) If map substitutes - * String for X, then the created method is: - * - *

-     * public String at(int i) {
-     *     return (String)super.elementAt(i);
-     * }
- * - *

By default, all the occurrences of the names of the class - * declaring at() and the superclass are replaced - * with the name of the class and the superclass that the - * created method is added to. - * This is done whichever map is null or not. - * To prevent this replacement, call ClassMap.fix() - * or put() to explicitly specify replacement. - * - *

Note: if the .class notation (for example, - * String.class) is included in an expression, the - * Javac compiler may produce a helper method. - * Since this constructor never - * copies this helper method, the programmers have the responsiblity of - * copying it. Otherwise, use Class.forName() in the - * expression. - * - * @param src the source method. - * @param declaring the class to which the created method is added. - * @param map the hashtable associating original class names - * with substituted names. - * It can be null. - * - * @see CtClass#addMethod(CtMethod) - * @see ClassMap#fix(String) - */ - public CtMethod(CtMethod src, CtClass declaring, ClassMap map) - throws CannotCompileException - { - this(null, declaring); - copy(src, false, map); - } - - /** - * Compiles the given source code and creates a method. - * This method simply delegates to make() in - * CtNewMethod. See it for more details. - * CtNewMethod has a number of useful factory methods. - * - * @param src the source text. - * @param declaring the class to which the created method is added. - * @see CtNewMethod#make(String, CtClass) - */ - public static CtMethod make(String src, CtClass declaring) - throws CannotCompileException - { - return CtNewMethod.make(src, declaring); - } - - /** - * Creates a method from a MethodInfo object. - * - * @param declaring the class declaring the method. - * @throws CannotCompileException if the the MethodInfo - * object and the declaring class have different - * ConstPool objects - * @since 3.6 - */ - public static CtMethod make(MethodInfo minfo, CtClass declaring) - throws CannotCompileException - { - if (declaring.getClassFile2().getConstPool() != minfo.getConstPool()) - throw new CannotCompileException("bad declaring class"); - - return new CtMethod(minfo, declaring); - } - - /** - * Returns a hash code value for the method. - * If two methods have the same name and signature, then - * the hash codes for the two methods are equal. - */ - public int hashCode() { - return getStringRep().hashCode(); - } - - /** - * This method is invoked when setName() or replaceClassName() - * in CtClass is called. - */ - void nameReplaced() { - cachedStringRep = null; - } - - /* This method is also called by CtClassType.getMethods0(). - */ - final String getStringRep() { - if (cachedStringRep == null) - cachedStringRep = methodInfo.getName() - + Descriptor.getParamDescriptor(methodInfo.getDescriptor()); - - return cachedStringRep; - } - - /** - * Indicates whether obj has the same name and the - * same signature as this method. - */ - public boolean equals(Object obj) { - return obj != null && obj instanceof CtMethod - && ((CtMethod)obj).getStringRep().equals(getStringRep()); - } - - /** - * Returns the method name followed by parameter types - * such as javassist.CtMethod.setBody(String). - * - * @since 3.5 - */ - public String getLongName() { - return getDeclaringClass().getName() + "." - + getName() + Descriptor.toString(getSignature()); - } - - /** - * Obtains the name of this method. - */ - public String getName() { - return methodInfo.getName(); - } - - /** - * Changes the name of this method. - */ - public void setName(String newname) { - declaringClass.checkModify(); - methodInfo.setName(newname); - } - - /** - * Obtains the type of the returned value. - */ - public CtClass getReturnType() throws NotFoundException { - return getReturnType0(); - } - - /** - * Returns true if the method body is empty, that is, {}. - * It also returns true if the method is an abstract method. - */ - public boolean isEmpty() { - CodeAttribute ca = getMethodInfo2().getCodeAttribute(); - if (ca == null) // abstract or native - return (getModifiers() & Modifier.ABSTRACT) != 0; - - CodeIterator it = ca.iterator(); - try { - return it.hasNext() && it.byteAt(it.next()) == Opcode.RETURN - && !it.hasNext(); - } - catch (BadBytecode e) {} - return false; - } - - /** - * Copies a method body from another method. - * If this method is abstract, the abstract modifier is removed - * after the method body is copied. - * - *

All occurrences of the class names in the copied method body - * are replaced with the names specified by - * map if map is not null. - * - * @param src the method that the body is copied from. - * @param map the hashtable associating original class names - * with substituted names. - * It can be null. - */ - public void setBody(CtMethod src, ClassMap map) - throws CannotCompileException - { - setBody0(src.declaringClass, src.methodInfo, - declaringClass, methodInfo, map); - } - - /** - * Replace a method body with a new method body wrapping the - * given method. - * - * @param mbody the wrapped method - * @param constParam the constant parameter given to - * the wrapped method - * (maybe null). - * - * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass) - */ - public void setWrappedBody(CtMethod mbody, ConstParameter constParam) - throws CannotCompileException - { - declaringClass.checkModify(); - - CtClass clazz = getDeclaringClass(); - CtClass[] params; - CtClass retType; - try { - params = getParameterTypes(); - retType = getReturnType(); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - - Bytecode code = CtNewWrappedMethod.makeBody(clazz, - clazz.getClassFile2(), - mbody, - params, retType, - constParam); - CodeAttribute cattr = code.toCodeAttribute(); - methodInfo.setCodeAttribute(cattr); - methodInfo.setAccessFlags(methodInfo.getAccessFlags() - & ~AccessFlag.ABSTRACT); - // rebuilding a stack map table is not needed. - } - - // inner classes - - /** - * Instances of this class represent a constant parameter. - * They are used to specify the parameter given to the methods - * created by CtNewMethod.wrapped(). - * - * @see CtMethod#setWrappedBody(CtMethod,CtMethod.ConstParameter) - * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass) - * @see CtNewConstructor#make(CtClass[],CtClass[],int,CtMethod,CtMethod.ConstParameter,CtClass) - */ - public static class ConstParameter { - /** - * Makes an integer constant. - * - * @param i the constant value. - */ - public static ConstParameter integer(int i) { - return new IntConstParameter(i); - } - - /** - * Makes a long integer constant. - * - * @param i the constant value. - */ - public static ConstParameter integer(long i) { - return new LongConstParameter(i); - } - - /** - * Makes an String constant. - * - * @param s the constant value. - */ - public static ConstParameter string(String s) { - return new StringConstParameter(s); - } - - ConstParameter() {} - - /** - * @return the size of the stack consumption. - */ - int compile(Bytecode code) throws CannotCompileException { - return 0; - } - - String descriptor() { - return defaultDescriptor(); - } - - /** - * @see CtNewWrappedMethod - */ - static String defaultDescriptor() { - return "([Ljava/lang/Object;)Ljava/lang/Object;"; - } - - /** - * Returns the descriptor for constructors. - * - * @see CtNewWrappedConstructor - */ - String constDescriptor() { - return defaultConstDescriptor(); - } - - /** - * Returns the default descriptor for constructors. - */ - static String defaultConstDescriptor() { - return "([Ljava/lang/Object;)V"; - } - } - - static class IntConstParameter extends ConstParameter { - int param; - - IntConstParameter(int i) { - param = i; - } - - int compile(Bytecode code) throws CannotCompileException { - code.addIconst(param); - return 1; - } - - String descriptor() { - return "([Ljava/lang/Object;I)Ljava/lang/Object;"; - } - - String constDescriptor() { - return "([Ljava/lang/Object;I)V"; - } - } - - static class LongConstParameter extends ConstParameter { - long param; - - LongConstParameter(long l) { - param = l; - } - - int compile(Bytecode code) throws CannotCompileException { - code.addLconst(param); - return 2; - } - - String descriptor() { - return "([Ljava/lang/Object;J)Ljava/lang/Object;"; - } - - String constDescriptor() { - return "([Ljava/lang/Object;J)V"; - } - } - - static class StringConstParameter extends ConstParameter { - String param; - - StringConstParameter(String s) { - param = s; - } - - int compile(Bytecode code) throws CannotCompileException { - code.addLdc(param); - return 1; - } - - String descriptor() { - return "([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;"; - } - - String constDescriptor() { - return "([Ljava/lang/Object;Ljava/lang/String;)V"; - } - } -} diff --git a/src/com/wenshuo/agent/javassist/CtNewClass.java b/src/com/wenshuo/agent/javassist/CtNewClass.java deleted file mode 100644 index 1db1989..0000000 --- a/src/com/wenshuo/agent/javassist/CtNewClass.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.DataOutputStream; -import java.io.IOException; -import com.wenshuo.agent.javassist.bytecode.ClassFile; - -class CtNewClass extends CtClassType { - /* true if the class is an interface. - */ - protected boolean hasConstructor; - - CtNewClass(String name, ClassPool cp, - boolean isInterface, CtClass superclass) { - super(name, cp); - wasChanged = true; - String superName; - if (isInterface || superclass == null) - superName = null; - else - superName = superclass.getName(); - - classfile = new ClassFile(isInterface, name, superName); - if (isInterface && superclass != null) - classfile.setInterfaces(new String[] { superclass.getName() }); - - setModifiers(Modifier.setPublic(getModifiers())); - hasConstructor = isInterface; - } - - protected void extendToString(StringBuffer buffer) { - if (hasConstructor) - buffer.append("hasConstructor "); - - super.extendToString(buffer); - } - - public void addConstructor(CtConstructor c) - throws CannotCompileException - { - hasConstructor = true; - super.addConstructor(c); - } - - public void toBytecode(DataOutputStream out) - throws CannotCompileException, IOException - { - if (!hasConstructor) - try { - inheritAllConstructors(); - hasConstructor = true; - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - - super.toBytecode(out); - } - - /** - * Adds constructors inhrited from the super class. - * - *

After this method is called, the class inherits all the - * constructors from the super class. The added constructor - * calls the super's constructor with the same signature. - */ - public void inheritAllConstructors() - throws CannotCompileException, NotFoundException - { - CtClass superclazz; - CtConstructor[] cs; - - superclazz = getSuperclass(); - cs = superclazz.getDeclaredConstructors(); - - int n = 0; - for (int i = 0; i < cs.length; ++i) { - CtConstructor c = cs[i]; - int mod = c.getModifiers(); - if (isInheritable(mod, superclazz)) { - CtConstructor cons - = CtNewConstructor.make(c.getParameterTypes(), - c.getExceptionTypes(), this); - cons.setModifiers(mod & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)); - addConstructor(cons); - ++n; - } - } - - if (n < 1) - throw new CannotCompileException( - "no inheritable constructor in " + superclazz.getName()); - - } - - private boolean isInheritable(int mod, CtClass superclazz) { - if (Modifier.isPrivate(mod)) - return false; - - if (Modifier.isPackage(mod)) { - String pname = getPackageName(); - String pname2 = superclazz.getPackageName(); - if (pname == null) - return pname2 == null; - else - return pname.equals(pname2); - } - - return true; - } -} diff --git a/src/com/wenshuo/agent/javassist/CtNewConstructor.java b/src/com/wenshuo/agent/javassist/CtNewConstructor.java deleted file mode 100644 index 18c929d..0000000 --- a/src/com/wenshuo/agent/javassist/CtNewConstructor.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.Javac; -import com.wenshuo.agent.javassist.compiler.CompileError; -import com.wenshuo.agent.javassist.CtMethod.ConstParameter; - -/** - * A collection of static methods for creating a CtConstructor. - * An instance of this class does not make any sense. - * - *

A class initializer (static constructor) cannot be created by the - * methods in this class. Call makeClassInitializer() in - * CtClass and append code snippet to the body of the class - * initializer obtained by makeClassInitializer(). - * - * @see CtClass#addConstructor(CtConstructor) - * @see CtClass#makeClassInitializer() - */ -public class CtNewConstructor { - /** - * Specifies that no parameters are passed to a super-class' - * constructor. That is, the default constructor is invoked. - */ - public static final int PASS_NONE = 0; // call super() - - /** - * Specifies that parameters are converted into an array of - * Object and passed to a super-class' - * constructor. - */ - public static final int PASS_ARRAY = 1; // an array of parameters - - /** - * Specifies that parameters are passed as is - * to a super-class' constructor. The signature of that - * constructor must be the same as that of the created constructor. - */ - public static final int PASS_PARAMS = 2; - - /** - * Compiles the given source code and creates a constructor. - * The source code must include not only the constructor body - * but the whole declaration. - * - * @param src the source text. - * @param declaring the class to which the created constructor is added. - */ - public static CtConstructor make(String src, CtClass declaring) - throws CannotCompileException - { - Javac compiler = new Javac(declaring); - try { - CtMember obj = compiler.compile(src); - if (obj instanceof CtConstructor) { - // a stack map table has been already created. - return (CtConstructor)obj; - } - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - - throw new CannotCompileException("not a constructor"); - } - - /** - * Creates a public constructor. - * - * @param parameters a list of the parameter types. - * @param exceptions a list of the exception types. - * @param body the source text of the constructor body. - * It must be a block surrounded by {}. - * If it is null, the substituted - * constructor body does nothing except calling - * super(). - * @param declaring the class to which the created method is added. - */ - public static CtConstructor make(CtClass[] parameters, - CtClass[] exceptions, - String body, CtClass declaring) - throws CannotCompileException - { - try { - CtConstructor cc = new CtConstructor(parameters, declaring); - cc.setExceptionTypes(exceptions); - cc.setBody(body); - return cc; - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - } - - /** - * Creates a copy of a constructor. - * This is a convenience method for calling - * {@link CtConstructor#CtConstructor(CtConstructor, CtClass, ClassMap) this constructor}. - * See the description of the constructor for particular behavior of the copying. - * - * @param c the copied constructor. - * @param declaring the class to which the created method is added. - * @param map the hash table associating original class names - * with substituted names. - * It can be null. - * - * @see CtConstructor#CtConstructor(CtConstructor,CtClass,ClassMap) - */ - public static CtConstructor copy(CtConstructor c, CtClass declaring, - ClassMap map) throws CannotCompileException { - return new CtConstructor(c, declaring, map); - } - - /** - * Creates a default (public) constructor. - * - *

The created constructor takes no parameter. It calls - * super(). - */ - public static CtConstructor defaultConstructor(CtClass declaring) - throws CannotCompileException - { - CtConstructor cons = new CtConstructor((CtClass[])null, declaring); - - ConstPool cp = declaring.getClassFile2().getConstPool(); - Bytecode code = new Bytecode(cp, 1, 1); - code.addAload(0); - try { - code.addInvokespecial(declaring.getSuperclass(), - "", "()V"); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - - code.add(Bytecode.RETURN); - - // no need to construct a stack map table. - cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute()); - return cons; - } - - /** - * Creates a public constructor that only calls a constructor - * in the super class. The created constructor receives parameters - * specified by parameters but calls the super's - * constructor without those parameters (that is, it calls the default - * constructor). - * - *

The parameters passed to the created constructor should be - * used for field initialization. CtField.Initializer - * objects implicitly insert initialization code in constructor - * bodies. - * - * @param parameters parameter types - * @param exceptions exception types - * @param declaring the class to which the created constructor - * is added. - * @see CtField.Initializer#byParameter(int) - */ - public static CtConstructor skeleton(CtClass[] parameters, - CtClass[] exceptions, CtClass declaring) - throws CannotCompileException - { - return make(parameters, exceptions, PASS_NONE, - null, null, declaring); - } - - /** - * Creates a public constructor that only calls a constructor - * in the super class. The created constructor receives parameters - * specified by parameters and calls the super's - * constructor with those parameters. - * - * @param parameters parameter types - * @param exceptions exception types - * @param declaring the class to which the created constructor - * is added. - */ - public static CtConstructor make(CtClass[] parameters, - CtClass[] exceptions, CtClass declaring) - throws CannotCompileException - { - return make(parameters, exceptions, PASS_PARAMS, - null, null, declaring); - } - - /** - * Creates a public constructor. - * - *

If howto is PASS_PARAMS, - * the created constructor calls the super's constructor with the - * same signature. The superclass must contain - * a constructor taking the same set of parameters as the created one. - * - *

If howto is PASS_NONE, - * the created constructor calls the super's default constructor. - * The superclass must contain a constructor taking no parameters. - * - *

If howto is PASS_ARRAY, - * the created constructor calls the super's constructor - * with the given parameters in the form of an array of - * Object. The signature of the super's constructor - * must be: - * - *

constructor(Object[] params, <type> cvalue)
-     * 
- * - *

Here, cvalue is the constant value specified - * by cparam. - * - *

If cparam is null, the signature - * must be: - * - *

constructor(Object[] params)
- * - *

If body is not null, a copy of that method is - * embedded in the body of the created constructor. - * The embedded method is executed after - * the super's constructor is called and the values of fields are - * initialized. Note that body must not - * be a constructor but a method. - * - *

Since the embedded method is wrapped - * in parameter-conversion code - * as in CtNewMethod.wrapped(), - * the constructor parameters are - * passed in the form of an array of Object. - * The method specified by body must have the - * signature shown below: - * - *

Object method(Object[] params, <type> cvalue)
- * - *

If cparam is null, the signature - * must be: - * - *

Object method(Object[] params)
- * - *

Although the type of the returned value is Object, - * the value must be always null. - * - *

Example: - * - *

-     * ClassPool pool = ... ;
-     * CtClass xclass = pool.makeClass("X");
-     * CtMethod method = pool.getMethod("Sample", "m");
-     * xclass.setSuperclass(pool.get("Y"));
-     * CtClass[] argTypes = { CtClass.intType };
-     * ConstParameter cparam = ConstParameter.string("test");
-     * CtConstructor c = CtNewConstructor.make(argTypes, null,
-     *                                  PASS_PARAMS, method, cparam, xclass);
-     * xclass.addConstructor(c);
- * - *

where the class Sample is as follows: - * - *

-     * public class Sample {
-     *     public Object m(Object[] args, String msg) {
-     *         System.out.println(msg);
-     *         return null;
-     *     }
-     * }
- * - *

This program produces the following class: - * - *

-     * public class X extends Y {
-     *     public X(int p0) {
-     *         super(p0);
-     *         String msg = "test";
-     *         Object[] args = new Object[] { p0 };
-     *         // begin of copied body
-     *         System.out.println(msg);
-     *         Object result = null;
-     *         // end
-     *     }
-     * }
- * - * @param parameters a list of the parameter types - * @param exceptions a list of the exceptions - * @param howto how to pass parameters to the super-class' - * constructor (PASS_NONE, - * PASS_ARRAY, - * or PASS_PARAMS) - * @param body appended body (may be null). - * It must be not a constructor but a method. - * @param cparam constant parameter (may be null.) - * @param declaring the class to which the created constructor - * is added. - * - * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass) - */ - public static CtConstructor make(CtClass[] parameters, - CtClass[] exceptions, int howto, - CtMethod body, ConstParameter cparam, - CtClass declaring) - throws CannotCompileException - { - return CtNewWrappedConstructor.wrapped(parameters, exceptions, - howto, body, cparam, declaring); - } -} diff --git a/src/com/wenshuo/agent/javassist/CtNewMethod.java b/src/com/wenshuo/agent/javassist/CtNewMethod.java deleted file mode 100644 index d55a464..0000000 --- a/src/com/wenshuo/agent/javassist/CtNewMethod.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.Javac; -import com.wenshuo.agent.javassist.compiler.CompileError; -import com.wenshuo.agent.javassist.CtMethod.ConstParameter; - -/** - * A collection of static methods for creating a CtMethod. - * An instance of this class does not make any sense. - * - * @see CtClass#addMethod(CtMethod) - */ -public class CtNewMethod { - - /** - * Compiles the given source code and creates a method. - * The source code must include not only the method body - * but the whole declaration, for example, - * - *
"public Object id(Object obj) { return obj; }"
- * - * @param src the source text. - * @param declaring the class to which the created method is added. - */ - public static CtMethod make(String src, CtClass declaring) - throws CannotCompileException - { - return make(src, declaring, null, null); - } - - /** - * Compiles the given source code and creates a method. - * The source code must include not only the method body - * but the whole declaration, for example, - * - *
"public Object id(Object obj) { return obj; }"
- * - *

If the source code includes $proceed(), then - * it is compiled into a method call on the specified object. - * - * @param src the source text. - * @param declaring the class to which the created method is added. - * @param delegateObj the source text specifying the object - * that is called on by $proceed(). - * @param delegateMethod the name of the method - * that is called by $proceed(). - */ - public static CtMethod make(String src, CtClass declaring, - String delegateObj, String delegateMethod) - throws CannotCompileException - { - Javac compiler = new Javac(declaring); - try { - if (delegateMethod != null) - compiler.recordProceed(delegateObj, delegateMethod); - - CtMember obj = compiler.compile(src); - if (obj instanceof CtMethod) - return (CtMethod)obj; - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - - throw new CannotCompileException("not a method"); - } - - /** - * Creates a public (non-static) method. The created method cannot - * be changed to a static method later. - * - * @param returnType the type of the returned value. - * @param mname the method name. - * @param parameters a list of the parameter types. - * @param exceptions a list of the exception types. - * @param body the source text of the method body. - * It must be a block surrounded by {}. - * If it is null, the created method - * does nothing except returning zero or null. - * @param declaring the class to which the created method is added. - * @see #make(int, CtClass, String, CtClass[], CtClass[], String, CtClass) - */ - public static CtMethod make(CtClass returnType, - String mname, CtClass[] parameters, - CtClass[] exceptions, - String body, CtClass declaring) - throws CannotCompileException - { - return make(Modifier.PUBLIC, returnType, mname, parameters, exceptions, - body, declaring); - } - - /** - * Creates a method. modifiers can contain - * Modifier.STATIC. - * - * @param modifiers access modifiers. - * @param returnType the type of the returned value. - * @param mname the method name. - * @param parameters a list of the parameter types. - * @param exceptions a list of the exception types. - * @param body the source text of the method body. - * It must be a block surrounded by {}. - * If it is null, the created method - * does nothing except returning zero or null. - * @param declaring the class to which the created method is added. - * - * @see Modifier - */ - public static CtMethod make(int modifiers, CtClass returnType, - String mname, CtClass[] parameters, - CtClass[] exceptions, - String body, CtClass declaring) - throws CannotCompileException - { - try { - CtMethod cm - = new CtMethod(returnType, mname, parameters, declaring); - cm.setModifiers(modifiers); - cm.setExceptionTypes(exceptions); - cm.setBody(body); - return cm; - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - } - - /** - * Creates a copy of a method. This method is provided for creating - * a new method based on an existing method. - * This is a convenience method for calling - * {@link CtMethod#CtMethod(CtMethod, CtClass, ClassMap) this constructor}. - * See the description of the constructor for particular behavior of the copying. - * - * @param src the source method. - * @param declaring the class to which the created method is added. - * @param map the hash table associating original class names - * with substituted names. - * It can be null. - * - * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap) - */ - public static CtMethod copy(CtMethod src, CtClass declaring, - ClassMap map) throws CannotCompileException { - return new CtMethod(src, declaring, map); - } - - /** - * Creates a copy of a method with a new name. - * This method is provided for creating - * a new method based on an existing method. - * This is a convenience method for calling - * {@link CtMethod#CtMethod(CtMethod, CtClass, ClassMap) this constructor}. - * See the description of the constructor for particular behavior of the copying. - * - * @param src the source method. - * @param name the name of the created method. - * @param declaring the class to which the created method is added. - * @param map the hash table associating original class names - * with substituted names. - * It can be null. - * - * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap) - */ - public static CtMethod copy(CtMethod src, String name, CtClass declaring, - ClassMap map) throws CannotCompileException { - CtMethod cm = new CtMethod(src, declaring, map); - cm.setName(name); - return cm; - } - - /** - * Creates a public abstract method. - * - * @param returnType the type of the returned value - * @param mname the method name - * @param parameters a list of the parameter types - * @param exceptions a list of the exception types - * @param declaring the class to which the created method is added. - * - * @see CtMethod#CtMethod(CtClass,String,CtClass[],CtClass) - */ - public static CtMethod abstractMethod(CtClass returnType, - String mname, - CtClass[] parameters, - CtClass[] exceptions, - CtClass declaring) - throws NotFoundException - { - CtMethod cm = new CtMethod(returnType, mname, parameters, declaring); - cm.setExceptionTypes(exceptions); - return cm; - } - - /** - * Creates a public getter method. The getter method returns the value - * of the specified field in the class to which this method is added. - * The created method is initially not static even if the field is - * static. Change the modifiers if the method should be static. - * - * @param methodName the name of the getter - * @param field the field accessed. - */ - public static CtMethod getter(String methodName, CtField field) - throws CannotCompileException - { - FieldInfo finfo = field.getFieldInfo2(); - String fieldType = finfo.getDescriptor(); - String desc = "()" + fieldType; - ConstPool cp = finfo.getConstPool(); - MethodInfo minfo = new MethodInfo(cp, methodName, desc); - minfo.setAccessFlags(AccessFlag.PUBLIC); - - Bytecode code = new Bytecode(cp, 2, 1); - try { - String fieldName = finfo.getName(); - if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) { - code.addAload(0); - code.addGetfield(Bytecode.THIS, fieldName, fieldType); - } - else - code.addGetstatic(Bytecode.THIS, fieldName, fieldType); - - code.addReturn(field.getType()); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - - minfo.setCodeAttribute(code.toCodeAttribute()); - CtClass cc = field.getDeclaringClass(); - // a stack map is not needed. - return new CtMethod(minfo, cc); - } - - /** - * Creates a public setter method. The setter method assigns the - * value of the first parameter to the specified field - * in the class to which this method is added. - * The created method is not static even if the field is - * static. You may not change it to be static - * by setModifiers() in CtBehavior. - * - * @param methodName the name of the setter - * @param field the field accessed. - */ - public static CtMethod setter(String methodName, CtField field) - throws CannotCompileException - { - FieldInfo finfo = field.getFieldInfo2(); - String fieldType = finfo.getDescriptor(); - String desc = "(" + fieldType + ")V"; - ConstPool cp = finfo.getConstPool(); - MethodInfo minfo = new MethodInfo(cp, methodName, desc); - minfo.setAccessFlags(AccessFlag.PUBLIC); - - Bytecode code = new Bytecode(cp, 3, 3); - try { - String fieldName = finfo.getName(); - if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) { - code.addAload(0); - code.addLoad(1, field.getType()); - code.addPutfield(Bytecode.THIS, fieldName, fieldType); - } - else { - code.addLoad(1, field.getType()); - code.addPutstatic(Bytecode.THIS, fieldName, fieldType); - } - - code.addReturn(null); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - - minfo.setCodeAttribute(code.toCodeAttribute()); - CtClass cc = field.getDeclaringClass(); - // a stack map is not needed. - return new CtMethod(minfo, cc); - } - - /** - * Creates a method forwarding to a delegate in - * a super class. The created method calls a method specified - * by delegate with all the parameters passed to the - * created method. If the delegate method returns a value, - * the created method returns that value to the caller. - * The delegate method must be declared in a super class. - * - *

The following method is an example of the created method. - * - *

-     * int f(int p, int q) {
-     *     return super.f(p, q);
-     * }
- * - *

The name of the created method can be changed by - * setName(). - * - * @param delegate the method that the created method forwards to. - * @param declaring the class to which the created method is - * added. - */ - public static CtMethod delegator(CtMethod delegate, CtClass declaring) - throws CannotCompileException - { - try { - return delegator0(delegate, declaring); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - } - - private static CtMethod delegator0(CtMethod delegate, CtClass declaring) - throws CannotCompileException, NotFoundException - { - MethodInfo deleInfo = delegate.getMethodInfo2(); - String methodName = deleInfo.getName(); - String desc = deleInfo.getDescriptor(); - ConstPool cp = declaring.getClassFile2().getConstPool(); - MethodInfo minfo = new MethodInfo(cp, methodName, desc); - minfo.setAccessFlags(deleInfo.getAccessFlags()); - - ExceptionsAttribute eattr = deleInfo.getExceptionsAttribute(); - if (eattr != null) - minfo.setExceptionsAttribute( - (ExceptionsAttribute)eattr.copy(cp, null)); - - Bytecode code = new Bytecode(cp, 0, 0); - boolean isStatic = Modifier.isStatic(delegate.getModifiers()); - CtClass deleClass = delegate.getDeclaringClass(); - CtClass[] params = delegate.getParameterTypes(); - int s; - if (isStatic) { - s = code.addLoadParameters(params, 0); - code.addInvokestatic(deleClass, methodName, desc); - } - else { - code.addLoad(0, deleClass); - s = code.addLoadParameters(params, 1); - code.addInvokespecial(deleClass, methodName, desc); - } - - code.addReturn(delegate.getReturnType()); - code.setMaxLocals(++s); - code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value - minfo.setCodeAttribute(code.toCodeAttribute()); - // a stack map is not needed. - return new CtMethod(minfo, declaring); - } - - /** - * Creates a wrapped method. The wrapped method receives parameters - * in the form of an array of Object. - * - *

The body of the created method is a copy of the body of the method - * specified by body. However, it is wrapped in - * parameter-conversion code. - * - *

The method specified by body must have this singature: - * - *

Object method(Object[] params, <type> cvalue)
- * - *

The type of the cvalue depends on - * constParam. - * If constParam is null, the signature - * must be: - * - *

Object method(Object[] params)
- * - *

The method body copied from body is wrapped in - * parameter-conversion code, which converts parameters specified by - * parameterTypes into an array of Object. - * The returned value is also converted from the Object - * type to the type specified by returnType. Thus, - * the resulting method body is as follows: - * - *

-     * Object[] params = new Object[] { p0, p1, ... };
-     * <type> cvalue = <constant-value>;
-     *  ... copied method body ...
-     * Object result = <returned value>
-     * return (<returnType>)result;
-     * 
- * - *

The variables p0, p2, ... represent - * formal parameters of the created method. - * The value of cvalue is specified by - * constParam. - * - *

If the type of a parameter or a returned value is a primitive - * type, then the value is converted into a wrapper object such as - * java.lang.Integer. If the type of the returned value - * is void, the returned value is discarded. - * - *

Example: - * - *

-     * ClassPool pool = ... ;
-     * CtClass vec = pool.makeClass("intVector");
-     * vec.setSuperclass(pool.get("java.util.Vector"));
-     * CtMethod addMethod = pool.getMethod("Sample", "add0");
-     *
-     * CtClass[] argTypes = { CtClass.intType };
-     * CtMethod m = CtNewMethod.wrapped(CtClass.voidType, "add", argTypes,
-     *                                  null, addMethod, null, vec);
-     * vec.addMethod(m);
- * - *

where the class Sample is as follows: - * - *

public class Sample extends java.util.Vector {
-     *     public Object add0(Object[] args) {
-     *         super.addElement(args[0]);
-     *         return null;
-     *     }
-     * }
- * - *

This program produces a class intVector: - * - *

public class intVector extends java.util.Vector {
-     *     public void add(int p0) {
-     *         Object[] args = new Object[] { p0 };
-     *         // begin of the copied body
-     *         super.addElement(args[0]);
-     *         Object result = null;
-     *         // end
-     *     }
-     * }
- * - *

Note that the type of the parameter to add() depends - * only on the value of argTypes passed to - * CtNewMethod.wrapped(). Thus, it is easy to - * modify this program to produce a - * StringVector class, which is a vector containing - * only String objects, and other vector classes. - * - * @param returnType the type of the returned value. - * @param mname the method name. - * @param parameterTypes a list of the parameter types. - * @param exceptionTypes a list of the exception types. - * @param body the method body - * (must not be a static method). - * @param constParam the constant parameter - * (maybe null). - * @param declaring the class to which the created method is - * added. - */ - public static CtMethod wrapped(CtClass returnType, - String mname, - CtClass[] parameterTypes, - CtClass[] exceptionTypes, - CtMethod body, ConstParameter constParam, - CtClass declaring) - throws CannotCompileException - { - return CtNewWrappedMethod.wrapped(returnType, mname, parameterTypes, - exceptionTypes, body, constParam, declaring); - } -} diff --git a/src/com/wenshuo/agent/javassist/CtNewNestedClass.java b/src/com/wenshuo/agent/javassist/CtNewNestedClass.java deleted file mode 100644 index 4fb89b0..0000000 --- a/src/com/wenshuo/agent/javassist/CtNewNestedClass.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.ClassFile; -import com.wenshuo.agent.javassist.bytecode.AccessFlag; -import com.wenshuo.agent.javassist.bytecode.InnerClassesAttribute; - -/** - * A newly created public nested class. - */ -class CtNewNestedClass extends CtNewClass { - CtNewNestedClass(String realName, ClassPool cp, boolean isInterface, - CtClass superclass) { - super(realName, cp, isInterface, superclass); - } - - /** - * This method does not change the STATIC bit. The original value is kept. - */ - public void setModifiers(int mod) { - mod = mod & ~Modifier.STATIC; - super.setModifiers(mod); - updateInnerEntry(mod, getName(), this, true); - } - - private static void updateInnerEntry(int mod, String name, CtClass clazz, boolean outer) { - ClassFile cf = clazz.getClassFile2(); - InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( - InnerClassesAttribute.tag); - if (ica == null) - return; - - int n = ica.tableLength(); - for (int i = 0; i < n; i++) - if (name.equals(ica.innerClass(i))) { - int acc = ica.accessFlags(i) & AccessFlag.STATIC; - ica.setAccessFlags(i, mod | acc); - String outName = ica.outerClass(i); - if (outName != null && outer) - try { - CtClass parent = clazz.getClassPool().get(outName); - updateInnerEntry(mod, name, parent, false); - } - catch (NotFoundException e) { - throw new RuntimeException("cannot find the declaring class: " - + outName); - } - - break; - } - } -} diff --git a/src/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java b/src/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java deleted file mode 100644 index d507162..0000000 --- a/src/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.CtMethod.ConstParameter; - -class CtNewWrappedConstructor extends CtNewWrappedMethod { - private static final int PASS_NONE = CtNewConstructor.PASS_NONE; - // private static final int PASS_ARRAY = CtNewConstructor.PASS_ARRAY; - private static final int PASS_PARAMS = CtNewConstructor.PASS_PARAMS; - - public static CtConstructor wrapped(CtClass[] parameterTypes, - CtClass[] exceptionTypes, - int howToCallSuper, - CtMethod body, - ConstParameter constParam, - CtClass declaring) - throws CannotCompileException - { - try { - CtConstructor cons = new CtConstructor(parameterTypes, declaring); - cons.setExceptionTypes(exceptionTypes); - Bytecode code = makeBody(declaring, declaring.getClassFile2(), - howToCallSuper, body, - parameterTypes, constParam); - cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute()); - // a stack map table is not needed. - return cons; - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - } - - protected static Bytecode makeBody(CtClass declaring, ClassFile classfile, - int howToCallSuper, - CtMethod wrappedBody, - CtClass[] parameters, - ConstParameter cparam) - throws CannotCompileException - { - int stacksize, stacksize2; - - int superclazz = classfile.getSuperclassId(); - Bytecode code = new Bytecode(classfile.getConstPool(), 0, 0); - code.setMaxLocals(false, parameters, 0); - code.addAload(0); - if (howToCallSuper == PASS_NONE) { - stacksize = 1; - code.addInvokespecial(superclazz, "", "()V"); - } - else if (howToCallSuper == PASS_PARAMS) { - stacksize = code.addLoadParameters(parameters, 1) + 1; - code.addInvokespecial(superclazz, "", - Descriptor.ofConstructor(parameters)); - } - else { - stacksize = compileParameterList(code, parameters, 1); - String desc; - if (cparam == null) { - stacksize2 = 2; - desc = ConstParameter.defaultConstDescriptor(); - } - else { - stacksize2 = cparam.compile(code) + 2; - desc = cparam.constDescriptor(); - } - - if (stacksize < stacksize2) - stacksize = stacksize2; - - code.addInvokespecial(superclazz, "", desc); - } - - if (wrappedBody == null) - code.add(Bytecode.RETURN); - else { - stacksize2 = makeBody0(declaring, classfile, wrappedBody, - false, parameters, CtClass.voidType, - cparam, code); - if (stacksize < stacksize2) - stacksize = stacksize2; - } - - code.setMaxStack(stacksize); - return code; - } -} diff --git a/src/com/wenshuo/agent/javassist/CtNewWrappedMethod.java b/src/com/wenshuo/agent/javassist/CtNewWrappedMethod.java deleted file mode 100644 index da2ad8b..0000000 --- a/src/com/wenshuo/agent/javassist/CtNewWrappedMethod.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.JvstCodeGen; -import java.util.Hashtable; -import com.wenshuo.agent.javassist.CtMethod.ConstParameter; - -class CtNewWrappedMethod { - - private static final String addedWrappedMethod = "_added_m$"; - - public static CtMethod wrapped(CtClass returnType, String mname, - CtClass[] parameterTypes, - CtClass[] exceptionTypes, - CtMethod body, ConstParameter constParam, - CtClass declaring) - throws CannotCompileException - { - CtMethod mt = new CtMethod(returnType, mname, parameterTypes, - declaring); - mt.setModifiers(body.getModifiers()); - try { - mt.setExceptionTypes(exceptionTypes); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - - Bytecode code = makeBody(declaring, declaring.getClassFile2(), body, - parameterTypes, returnType, constParam); - MethodInfo minfo = mt.getMethodInfo2(); - minfo.setCodeAttribute(code.toCodeAttribute()); - // a stack map has been already created. - return mt; - } - - static Bytecode makeBody(CtClass clazz, ClassFile classfile, - CtMethod wrappedBody, - CtClass[] parameters, - CtClass returnType, - ConstParameter cparam) - throws CannotCompileException - { - boolean isStatic = Modifier.isStatic(wrappedBody.getModifiers()); - Bytecode code = new Bytecode(classfile.getConstPool(), 0, 0); - int stacksize = makeBody0(clazz, classfile, wrappedBody, isStatic, - parameters, returnType, cparam, code); - code.setMaxStack(stacksize); - code.setMaxLocals(isStatic, parameters, 0); - return code; - } - - /* The generated method body does not need a stack map table - * because it does not contain a branch instruction. - */ - protected static int makeBody0(CtClass clazz, ClassFile classfile, - CtMethod wrappedBody, - boolean isStatic, CtClass[] parameters, - CtClass returnType, ConstParameter cparam, - Bytecode code) - throws CannotCompileException - { - if (!(clazz instanceof CtClassType)) - throw new CannotCompileException("bad declaring class" - + clazz.getName()); - - if (!isStatic) - code.addAload(0); - - int stacksize = compileParameterList(code, parameters, - (isStatic ? 0 : 1)); - int stacksize2; - String desc; - if (cparam == null) { - stacksize2 = 0; - desc = ConstParameter.defaultDescriptor(); - } - else { - stacksize2 = cparam.compile(code); - desc = cparam.descriptor(); - } - - checkSignature(wrappedBody, desc); - - String bodyname; - try { - bodyname = addBodyMethod((CtClassType)clazz, classfile, - wrappedBody); - /* if an exception is thrown below, the method added above - * should be removed. (future work :<) - */ - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - - if (isStatic) - code.addInvokestatic(Bytecode.THIS, bodyname, desc); - else - code.addInvokespecial(Bytecode.THIS, bodyname, desc); - - compileReturn(code, returnType); // consumes 2 stack entries - - if (stacksize < stacksize2 + 2) - stacksize = stacksize2 + 2; - - return stacksize; - } - - private static void checkSignature(CtMethod wrappedBody, - String descriptor) - throws CannotCompileException - { - if (!descriptor.equals(wrappedBody.getMethodInfo2().getDescriptor())) - throw new CannotCompileException( - "wrapped method with a bad signature: " - + wrappedBody.getDeclaringClass().getName() - + '.' + wrappedBody.getName()); - } - - private static String addBodyMethod(CtClassType clazz, - ClassFile classfile, - CtMethod src) - throws BadBytecode, CannotCompileException - { - Hashtable bodies = clazz.getHiddenMethods(); - String bodyname = (String)bodies.get(src); - if (bodyname == null) { - do { - bodyname = addedWrappedMethod + clazz.getUniqueNumber(); - } while (classfile.getMethod(bodyname) != null); - ClassMap map = new ClassMap(); - map.put(src.getDeclaringClass().getName(), clazz.getName()); - MethodInfo body = new MethodInfo(classfile.getConstPool(), - bodyname, src.getMethodInfo2(), - map); - int acc = body.getAccessFlags(); - body.setAccessFlags(AccessFlag.setPrivate(acc)); - body.addAttribute(new SyntheticAttribute(classfile.getConstPool())); - // a stack map is copied. rebuilding it is not needed. - classfile.addMethod(body); - bodies.put(src, bodyname); - CtMember.Cache cache = clazz.hasMemberCache(); - if (cache != null) - cache.addMethod(new CtMethod(body, clazz)); - } - - return bodyname; - } - - /* compileParameterList() returns the stack size used - * by the produced code. - * - * @param regno the index of the local variable in which - * the first argument is received. - * (0: static method, 1: regular method.) - */ - static int compileParameterList(Bytecode code, - CtClass[] params, int regno) { - return JvstCodeGen.compileParameterList(code, params, regno); - } - - /* - * The produced codes cosume 1 or 2 stack entries. - */ - private static void compileReturn(Bytecode code, CtClass type) { - if (type.isPrimitive()) { - CtPrimitiveType pt = (CtPrimitiveType)type; - if (pt != CtClass.voidType) { - String wrapper = pt.getWrapperName(); - code.addCheckcast(wrapper); - code.addInvokevirtual(wrapper, pt.getGetMethodName(), - pt.getGetMethodDescriptor()); - } - - code.addOpcode(pt.getReturnOp()); - } - else { - code.addCheckcast(type); - code.addOpcode(Bytecode.ARETURN); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/CtPrimitiveType.java b/src/com/wenshuo/agent/javassist/CtPrimitiveType.java deleted file mode 100644 index bdd961b..0000000 --- a/src/com/wenshuo/agent/javassist/CtPrimitiveType.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -/** - * An instance of CtPrimitiveType represents a primitive type. - * It is obtained from CtClass. - */ -public final class CtPrimitiveType extends CtClass { - private char descriptor; - private String wrapperName; - private String getMethodName; - private String mDescriptor; - private int returnOp; - private int arrayType; - private int dataSize; - - CtPrimitiveType(String name, char desc, String wrapper, - String methodName, String mDesc, int opcode, int atype, - int size) { - super(name); - descriptor = desc; - wrapperName = wrapper; - getMethodName = methodName; - mDescriptor = mDesc; - returnOp = opcode; - arrayType = atype; - dataSize = size; - } - - /** - * Returns true if this object represents a primitive - * Java type: boolean, byte, char, short, int, long, float, double, - * or void. - */ - public boolean isPrimitive() { return true; } - - /** - * Returns the modifiers for this type. - * For decoding, use javassist.Modifier. - * - * @see Modifier - */ - public int getModifiers() { - return Modifier.PUBLIC | Modifier.FINAL; - } - - /** - * Returns the descriptor representing this type. - * For example, if the type is int, then the descriptor is I. - */ - public char getDescriptor() { return descriptor; } - - /** - * Returns the name of the wrapper class. - * For example, if the type is int, then the wrapper class is - * java.lang.Integer. - */ - public String getWrapperName() { return wrapperName; } - - /** - * Returns the name of the method for retrieving the value - * from the wrapper object. - * For example, if the type is int, then the method name is - * intValue. - */ - public String getGetMethodName() { return getMethodName; } - - /** - * Returns the descriptor of the method for retrieving the value - * from the wrapper object. - * For example, if the type is int, then the method descriptor is - * ()I. - */ - public String getGetMethodDescriptor() { return mDescriptor; } - - /** - * Returns the opcode for returning a value of the type. - * For example, if the type is int, then the returned opcode is - * javassit.bytecode.Opcode.IRETURN. - */ - public int getReturnOp() { return returnOp; } - - /** - * Returns the array-type code representing the type. - * It is used for the newarray instruction. - * For example, if the type is int, then this method returns - * javassit.bytecode.Opcode.T_INT. - */ - public int getArrayType() { return arrayType; } - - /** - * Returns the data size of the primitive type. - * If the type is long or double, this method returns 2. - * Otherwise, it returns 1. - */ - public int getDataSize() { return dataSize; } -} diff --git a/src/com/wenshuo/agent/javassist/Loader.java b/src/com/wenshuo/agent/javassist/Loader.java deleted file mode 100644 index 2c2e52f..0000000 --- a/src/com/wenshuo/agent/javassist/Loader.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.*; -import java.util.Hashtable; -import java.util.Vector; -import java.security.ProtectionDomain; - -/** - * The class loader for Javassist. - * - *

This is a sample class loader using ClassPool. - * Unlike a regular class loader, this class loader obtains bytecode - * from a ClassPool. - * - *

Note that Javassist can be used without this class loader; programmers - * can define their own versions of class loader. They can run - * a program even without any user-defined class loader if that program - * is statically translated with Javassist. - * This class loader is just provided as a utility class. - * - *

Suppose that an instance of MyTranslator implementing - * the interface Translator is responsible for modifying - * class files. - * The startup program of an application using MyTranslator - * should be something like this: - * - *

- * import com.wenshuo.agent.javassist.*;
- *
- * public class Main {
- *   public static void main(String[] args) throws Throwable {
- *     MyTranslator myTrans = new MyTranslator();
- *     ClassPool cp = ClassPool.getDefault();
- *     Loader cl = new Loader(cp);
- *     cl.addTranslator(cp, myTrans);
- *     cl.run("MyApp", args);
- *   }
- * }
- * 
- * - *

Class MyApp is the main program of the application. - * - *

This program should be executed as follows: - * - *

- * % java Main arg1 arg2...
- * 
- * - *

It modifies the class MyApp with a MyTranslator - * object before the JVM loads it. - * Then it calls main() in MyApp with arguments - * arg1, arg2, ... - * - *

This program execution is equivalent to: - * - *

- * % java MyApp arg1 arg2...
- * 
- * - *

except that classes are translated by MyTranslator - * at load time. - * - *

If only a particular class must be modified when it is loaded, - * the startup program can be simpler; MyTranslator is - * unnecessary. For example, if only a class test.Rectangle - * is modified, the main() method above will be the following: - * - *

- * ClassPool cp = ClassPool.getDefault();
- * Loader cl = new Loader(cp);
- * CtClass ct = cp.get("test.Rectangle");
- * ct.setSuperclass(cp.get("test.Point"));
- * cl.run("MyApp", args);
- * - *

This program changes the super class of the test.Rectangle - * class. - * - *

Note 1: - * - *

This class loader does not allow the users to intercept the loading - * of java.* and javax.* classes (and - * sun.*, org.xml.*, ...) unless - * Loader.doDelegation is false. This is because - * the JVM prohibits a user class loader from loading a system class. - * Also see Note 2. - * If this behavior is not appropriate, a subclass of Loader - * must be defined and loadClassByDelegation() must be overridden. - * - *

Note 2: - * - *

If classes are loaded with different class loaders, they belong to - * separate name spaces. If class C is loaded by a class - * loader CL, all classes that the class C - * refers to are also loaded by CL. However, if CL - * delegates the loading of the class C to CL', - * then those classes that the class C refers to - * are loaded by a parent class loader CL' - * instead of CL. - * - *

If an object of class C is assigned - * to a variable of class C belonging to a different name - * space, then a ClassCastException is thrown. - * - *

Because of the fact above, this loader delegates only the loading of - * javassist.Loader - * and classes included in package java.* and - * javax.* to the parent class - * loader. Other classes are directly loaded by this loader. - * - *

For example, suppose that java.lang.String would be loaded - * by this loader while java.io.File is loaded by the parent - * class loader. If the constructor of java.io.File is called - * with an instance of java.lang.String, then it may throw - * an exception since it accepts an instance of only the - * java.lang.String loaded by the parent class loader. - * - * @see javassist.ClassPool - * @see javassist.Translator - */ -public class Loader extends ClassLoader { - private Hashtable notDefinedHere; // must be atomic. - private Vector notDefinedPackages; // must be atomic. - private ClassPool source; - private Translator translator; - private ProtectionDomain domain; - - /** - * Specifies the algorithm of class loading. - * - *

This class loader uses the parent class loader for - * java.* and javax.* classes. - * If this variable doDelegation - * is false, this class loader does not delegate those - * classes to the parent class loader. - * - *

The default value is true. - */ - public boolean doDelegation = true; - - /** - * Creates a new class loader. - */ - public Loader() { - this(null); - } - - /** - * Creates a new class loader. - * - * @param cp the source of class files. - */ - public Loader(ClassPool cp) { - init(cp); - } - - /** - * Creates a new class loader - * using the specified parent class loader for delegation. - * - * @param parent the parent class loader. - * @param cp the source of class files. - */ - public Loader(ClassLoader parent, ClassPool cp) { - super(parent); - init(cp); - } - - private void init(ClassPool cp) { - notDefinedHere = new Hashtable(); - notDefinedPackages = new Vector(); - source = cp; - translator = null; - domain = null; - delegateLoadingOf("javassist.Loader"); - } - - /** - * Records a class so that the loading of that class is delegated - * to the parent class loader. - * - *

If the given class name ends with . (dot), then - * that name is interpreted as a package name. All the classes - * in that package and the sub packages are delegated. - */ - public void delegateLoadingOf(String classname) { - if (classname.endsWith(".")) - notDefinedPackages.addElement(classname); - else - notDefinedHere.put(classname, this); - } - - /** - * Sets the protection domain for the classes handled by this class - * loader. Without registering an appropriate protection domain, - * the program loaded by this loader will not work with a security - * manager or a signed jar file. - */ - public void setDomain(ProtectionDomain d) { - domain = d; - } - - /** - * Sets the soruce ClassPool. - */ - public void setClassPool(ClassPool cp) { - source = cp; - } - - /** - * Adds a translator, which is called whenever a class is loaded. - * - * @param cp the ClassPool object for obtaining - * a class file. - * @param t a translator. - * @throws NotFoundException if t.start() throws an exception. - * @throws CannotCompileException if t.start() throws an exception. - */ - public void addTranslator(ClassPool cp, Translator t) - throws NotFoundException, CannotCompileException { - source = cp; - translator = t; - t.start(cp); - } - - /** - * Loads a class with an instance of Loader - * and calls main() of that class. - * - *

This method calls run(). - * - * @param args command line parameters. - *
  {@code args[0]} is the class name to be loaded. - *
  {@code args[1..n]} are parameters passed - * to the target {@code main()}. - * - * @see javassist.Loader#run(String[]) - */ - public static void main(String[] args) throws Throwable { - Loader cl = new Loader(); - cl.run(args); - } - - /** - * Loads a class and calls main() in that class. - * - * @param args command line parameters. - * - *
  {@code args[0]} is the class name to be loaded. - *
  {@code args[1..n]} are parameters passed - * to the target {@code main()}. - */ - public void run(String[] args) throws Throwable { - int n = args.length - 1; - if (n >= 0) { - String[] args2 = new String[n]; - for (int i = 0; i < n; ++i) - args2[i] = args[i + 1]; - - run(args[0], args2); - } - } - - /** - * Loads a class and calls main() in that class. - * - * @param classname the loaded class. - * @param args parameters passed to main(). - */ - public void run(String classname, String[] args) throws Throwable { - Class c = loadClass(classname); - try { - c.getDeclaredMethod("main", new Class[] { String[].class }).invoke( - null, - new Object[] { args }); - } - catch (java.lang.reflect.InvocationTargetException e) { - throw e.getTargetException(); - } - } - - /** - * Requests the class loader to load a class. - */ - protected Class loadClass(String name, boolean resolve) - throws ClassFormatError, ClassNotFoundException { - name = name.intern(); - synchronized (name) { - Class c = findLoadedClass(name); - if (c == null) - c = loadClassByDelegation(name); - - if (c == null) - c = findClass(name); - - if (c == null) - c = delegateToParent(name); - - if (resolve) - resolveClass(c); - - return c; - } - } - - /** - * Finds the specified class using ClassPath. - * If the source throws an exception, this returns null. - * - *

This method can be overridden by a subclass of - * Loader. Note that the overridden method must not throw - * an exception when it just fails to find a class file. - * - * @return null if the specified class could not be found. - * @throws ClassNotFoundException if an exception is thrown while - * obtaining a class file. - */ - protected Class findClass(String name) throws ClassNotFoundException { - byte[] classfile; - try { - if (source != null) { - if (translator != null) - translator.onLoad(source, name); - - try { - classfile = source.get(name).toBytecode(); - } - catch (NotFoundException e) { - return null; - } - } - else { - String jarname = "/" + name.replace('.', '/') + ".class"; - InputStream in = this.getClass().getResourceAsStream(jarname); - if (in == null) - return null; - - classfile = ClassPoolTail.readStream(in); - } - } - catch (Exception e) { - throw new ClassNotFoundException( - "caught an exception while obtaining a class file for " - + name, e); - } - - int i = name.lastIndexOf('.'); - if (i != -1) { - String pname = name.substring(0, i); - if (getPackage(pname) == null) - try { - definePackage( - pname, null, null, null, null, null, null, null); - } - catch (IllegalArgumentException e) { - // ignore. maybe the package object for the same - // name has been created just right away. - } - } - - if (domain == null) - return defineClass(name, classfile, 0, classfile.length); - else - return defineClass(name, classfile, 0, classfile.length, domain); - } - - protected Class loadClassByDelegation(String name) - throws ClassNotFoundException - { - /* The swing components must be loaded by a system - * class loader. - * javax.swing.UIManager loads a (concrete) subclass - * of LookAndFeel by a system class loader and cast - * an instance of the class to LookAndFeel for - * (maybe) a security reason. To avoid failure of - * type conversion, LookAndFeel must not be loaded - * by this class loader. - */ - - Class c = null; - if (doDelegation) - if (name.startsWith("java.") - || name.startsWith("javax.") - || name.startsWith("sun.") - || name.startsWith("com.sun.") - || name.startsWith("org.w3c.") - || name.startsWith("org.xml.") - || notDelegated(name)) - c = delegateToParent(name); - - return c; - } - - private boolean notDelegated(String name) { - if (notDefinedHere.get(name) != null) - return true; - - int n = notDefinedPackages.size(); - for (int i = 0; i < n; ++i) - if (name.startsWith((String)notDefinedPackages.elementAt(i))) - return true; - - return false; - } - - protected Class delegateToParent(String classname) - throws ClassNotFoundException - { - ClassLoader cl = getParent(); - if (cl != null) - return cl.loadClass(classname); - else - return findSystemClass(classname); - } -} diff --git a/src/com/wenshuo/agent/javassist/LoaderClassPath.java b/src/com/wenshuo/agent/javassist/LoaderClassPath.java deleted file mode 100644 index e7c1029..0000000 --- a/src/com/wenshuo/agent/javassist/LoaderClassPath.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.InputStream; -import java.net.URL; -import java.lang.ref.WeakReference; - -/** - * A class search-path representing a class loader. - * - *

It is used for obtaining a class file from the given - * class loader by getResourceAsStream(). - * The LoaderClassPath refers to the class loader through - * WeakReference. If the class loader is garbage collected, - * the other search pathes are examined. - * - *

The given class loader must have both getResourceAsStream() - * and getResource(). - * - * @author Bill Burke - * @author Shigeru Chiba - * - * @see ClassPool#insertClassPath(ClassPath) - * @see ClassPool#appendClassPath(ClassPath) - * @see ClassClassPath - */ -public class LoaderClassPath implements ClassPath { - private WeakReference clref; - - /** - * Creates a search path representing a class loader. - */ - public LoaderClassPath(ClassLoader cl) { - clref = new WeakReference(cl); - } - - public String toString() { - Object cl = null; - if (clref != null) - cl = clref.get(); - - return cl == null ? "" : cl.toString(); - } - - /** - * Obtains a class file from the class loader. - * This method calls getResourceAsStream(String) - * on the class loader. - */ - public InputStream openClassfile(String classname) { - String cname = classname.replace('.', '/') + ".class"; - ClassLoader cl = (ClassLoader)clref.get(); - if (cl == null) - return null; // not found - else - return cl.getResourceAsStream(cname); - } - - /** - * Obtains the URL of the specified class file. - * This method calls getResource(String) - * on the class loader. - * - * @return null if the class file could not be found. - */ - public URL find(String classname) { - String cname = classname.replace('.', '/') + ".class"; - ClassLoader cl = (ClassLoader)clref.get(); - if (cl == null) - return null; // not found - else - return cl.getResource(cname); - } - - /** - * Closes this class path. - */ - public void close() { - clref = null; - } -} diff --git a/src/com/wenshuo/agent/javassist/Modifier.java b/src/com/wenshuo/agent/javassist/Modifier.java deleted file mode 100644 index 92f709c..0000000 --- a/src/com/wenshuo/agent/javassist/Modifier.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import com.wenshuo.agent.javassist.bytecode.AccessFlag; - -/** - * The Modifier class provides static methods and constants to decode - * class and member access modifiers. The constant values are equivalent - * to the corresponding values in javassist.bytecode.AccessFlag. - * - *

All the methods/constants in this class are compatible with - * ones in java.lang.reflect.Modifier. - * - * @see CtClass#getModifiers() - */ -public class Modifier { - public static final int PUBLIC = AccessFlag.PUBLIC; - public static final int PRIVATE = AccessFlag.PRIVATE; - public static final int PROTECTED = AccessFlag.PROTECTED; - public static final int STATIC = AccessFlag.STATIC; - public static final int FINAL = AccessFlag.FINAL; - public static final int SYNCHRONIZED = AccessFlag.SYNCHRONIZED; - public static final int VOLATILE = AccessFlag.VOLATILE; - public static final int VARARGS = AccessFlag.VARARGS; - public static final int TRANSIENT = AccessFlag.TRANSIENT; - public static final int NATIVE = AccessFlag.NATIVE; - public static final int INTERFACE = AccessFlag.INTERFACE; - public static final int ABSTRACT = AccessFlag.ABSTRACT; - public static final int STRICT = AccessFlag.STRICT; - public static final int ANNOTATION = AccessFlag.ANNOTATION; - public static final int ENUM = AccessFlag.ENUM; - - /** - * Returns true if the modifiers include the public - * modifier. - */ - public static boolean isPublic(int mod) { - return (mod & PUBLIC) != 0; - } - - /** - * Returns true if the modifiers include the private - * modifier. - */ - public static boolean isPrivate(int mod) { - return (mod & PRIVATE) != 0; - } - - /** - * Returns true if the modifiers include the protected - * modifier. - */ - public static boolean isProtected(int mod) { - return (mod & PROTECTED) != 0; - } - - /** - * Returns true if the modifiers do not include either - * public, protected, or private. - */ - public static boolean isPackage(int mod) { - return (mod & (PUBLIC | PRIVATE | PROTECTED)) == 0; - } - - /** - * Returns true if the modifiers include the static - * modifier. - */ - public static boolean isStatic(int mod) { - return (mod & STATIC) != 0; - } - - /** - * Returns true if the modifiers include the final - * modifier. - */ - public static boolean isFinal(int mod) { - return (mod & FINAL) != 0; - } - - /** - * Returns true if the modifiers include the synchronized - * modifier. - */ - public static boolean isSynchronized(int mod) { - return (mod & SYNCHRONIZED) != 0; - } - - /** - * Returns true if the modifiers include the volatile - * modifier. - */ - public static boolean isVolatile(int mod) { - return (mod & VOLATILE) != 0; - } - - /** - * Returns true if the modifiers include the transient - * modifier. - */ - public static boolean isTransient(int mod) { - return (mod & TRANSIENT) != 0; - } - - /** - * Returns true if the modifiers include the native - * modifier. - */ - public static boolean isNative(int mod) { - return (mod & NATIVE) != 0; - } - - /** - * Returns true if the modifiers include the interface - * modifier. - */ - public static boolean isInterface(int mod) { - return (mod & INTERFACE) != 0; - } - - /** - * Returns true if the modifiers include the annotation - * modifier. - * - * @since 3.2 - */ - public static boolean isAnnotation(int mod) { - return (mod & ANNOTATION) != 0; - } - - /** - * Returns true if the modifiers include the enum - * modifier. - * - * @since 3.2 - */ - public static boolean isEnum(int mod) { - return (mod & ENUM) != 0; - } - - /** - * Returns true if the modifiers include the abstract - * modifier. - */ - public static boolean isAbstract(int mod) { - return (mod & ABSTRACT) != 0; - } - - /** - * Returns true if the modifiers include the strictfp - * modifier. - */ - public static boolean isStrict(int mod) { - return (mod & STRICT) != 0; - } - - /** - * Truns the public bit on. The protected and private bits are - * cleared. - */ - public static int setPublic(int mod) { - return (mod & ~(PRIVATE | PROTECTED)) | PUBLIC; - } - - /** - * Truns the protected bit on. The protected and public bits are - * cleared. - */ - public static int setProtected(int mod) { - return (mod & ~(PRIVATE | PUBLIC)) | PROTECTED; - } - - /** - * Truns the private bit on. The protected and private bits are - * cleared. - */ - public static int setPrivate(int mod) { - return (mod & ~(PROTECTED | PUBLIC)) | PRIVATE; - } - - /** - * Clears the public, protected, and private bits. - */ - public static int setPackage(int mod) { - return (mod & ~(PROTECTED | PUBLIC | PRIVATE)); - } - - /** - * Clears a specified bit in mod. - */ - public static int clear(int mod, int clearBit) { - return mod & ~clearBit; - } - - /** - * Return a string describing the access modifier flags in - * the specified modifier. - * - * @param mod modifier flags. - */ - public static String toString(int mod) { - return java.lang.reflect.Modifier.toString(mod); - } -} diff --git a/src/com/wenshuo/agent/javassist/NotFoundException.java b/src/com/wenshuo/agent/javassist/NotFoundException.java deleted file mode 100644 index 5569375..0000000 --- a/src/com/wenshuo/agent/javassist/NotFoundException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -/** - * Signals that something could not be found. - */ -public class NotFoundException extends Exception { - public NotFoundException(String msg) { - super(msg); - } - - public NotFoundException(String msg, Exception e) { - super(msg + " because of " + e.toString()); - } -} diff --git a/src/com/wenshuo/agent/javassist/SerialVersionUID.java b/src/com/wenshuo/agent/javassist/SerialVersionUID.java deleted file mode 100644 index 37f9add..0000000 --- a/src/com/wenshuo/agent/javassist/SerialVersionUID.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.*; -import java.lang.reflect.Modifier; - -import com.wenshuo.agent.javassist.bytecode.*; -import java.util.*; -import java.security.*; - -/** - * Utility for calculating serialVersionUIDs for Serializable classes. - * - * @author Bob Lee (crazybob@crazybob.org) - * @author modified by Shigeru Chiba - */ -public class SerialVersionUID { - - /** - * Adds serialVersionUID if one does not already exist. Call this before - * modifying a class to maintain serialization compatability. - */ - public static void setSerialVersionUID(CtClass clazz) - throws CannotCompileException, NotFoundException - { - // check for pre-existing field. - try { - clazz.getDeclaredField("serialVersionUID"); - return; - } - catch (NotFoundException e) {} - - // check if the class is serializable. - if (!isSerializable(clazz)) - return; - - // add field with default value. - CtField field = new CtField(CtClass.longType, "serialVersionUID", - clazz); - field.setModifiers(Modifier.PRIVATE | Modifier.STATIC | - Modifier.FINAL); - clazz.addField(field, calculateDefault(clazz) + "L"); - } - - /** - * Does the class implement Serializable? - */ - private static boolean isSerializable(CtClass clazz) - throws NotFoundException - { - ClassPool pool = clazz.getClassPool(); - return clazz.subtypeOf(pool.get("java.io.Serializable")); - } - - /** - * Calculate default value. See Java Serialization Specification, Stream - * Unique Identifiers. - * - * @since 3.20 - */ - public static long calculateDefault(CtClass clazz) - throws CannotCompileException - { - try { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(bout); - ClassFile classFile = clazz.getClassFile(); - - // class name. - String javaName = javaName(clazz); - out.writeUTF(javaName); - - CtMethod[] methods = clazz.getDeclaredMethods(); - - // class modifiers. - int classMods = clazz.getModifiers(); - if ((classMods & Modifier.INTERFACE) != 0) - if (methods.length > 0) - classMods = classMods | Modifier.ABSTRACT; - else - classMods = classMods & ~Modifier.ABSTRACT; - - out.writeInt(classMods); - - // interfaces. - String[] interfaces = classFile.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) - interfaces[i] = javaName(interfaces[i]); - - Arrays.sort(interfaces); - for (int i = 0; i < interfaces.length; i++) - out.writeUTF(interfaces[i]); - - // fields. - CtField[] fields = clazz.getDeclaredFields(); - Arrays.sort(fields, new Comparator() { - public int compare(Object o1, Object o2) { - CtField field1 = (CtField)o1; - CtField field2 = (CtField)o2; - return field1.getName().compareTo(field2.getName()); - } - }); - - for (int i = 0; i < fields.length; i++) { - CtField field = (CtField) fields[i]; - int mods = field.getModifiers(); - if (((mods & Modifier.PRIVATE) == 0) || - ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) { - out.writeUTF(field.getName()); - out.writeInt(mods); - out.writeUTF(field.getFieldInfo2().getDescriptor()); - } - } - - // static initializer. - if (classFile.getStaticInitializer() != null) { - out.writeUTF(""); - out.writeInt(Modifier.STATIC); - out.writeUTF("()V"); - } - - // constructors. - CtConstructor[] constructors = clazz.getDeclaredConstructors(); - Arrays.sort(constructors, new Comparator() { - public int compare(Object o1, Object o2) { - CtConstructor c1 = (CtConstructor)o1; - CtConstructor c2 = (CtConstructor)o2; - return c1.getMethodInfo2().getDescriptor().compareTo( - c2.getMethodInfo2().getDescriptor()); - } - }); - - for (int i = 0; i < constructors.length; i++) { - CtConstructor constructor = constructors[i]; - int mods = constructor.getModifiers(); - if ((mods & Modifier.PRIVATE) == 0) { - out.writeUTF(""); - out.writeInt(mods); - out.writeUTF(constructor.getMethodInfo2() - .getDescriptor().replace('/', '.')); - } - } - - // methods. - Arrays.sort(methods, new Comparator() { - public int compare(Object o1, Object o2) { - CtMethod m1 = (CtMethod)o1; - CtMethod m2 = (CtMethod)o2; - int value = m1.getName().compareTo(m2.getName()); - if (value == 0) - value = m1.getMethodInfo2().getDescriptor() - .compareTo(m2.getMethodInfo2().getDescriptor()); - - return value; - } - }); - - for (int i = 0; i < methods.length; i++) { - CtMethod method = methods[i]; - int mods = method.getModifiers() - & (Modifier.PUBLIC | Modifier.PRIVATE - | Modifier.PROTECTED | Modifier.STATIC - | Modifier.FINAL | Modifier.SYNCHRONIZED - | Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT); - if ((mods & Modifier.PRIVATE) == 0) { - out.writeUTF(method.getName()); - out.writeInt(mods); - out.writeUTF(method.getMethodInfo2() - .getDescriptor().replace('/', '.')); - } - } - - // calculate hash. - out.flush(); - MessageDigest digest = MessageDigest.getInstance("SHA"); - byte[] digested = digest.digest(bout.toByteArray()); - long hash = 0; - for (int i = Math.min(digested.length, 8) - 1; i >= 0; i--) - hash = (hash << 8) | (digested[i] & 0xFF); - - return hash; - } - catch (IOException e) { - throw new CannotCompileException(e); - } - catch (NoSuchAlgorithmException e) { - throw new CannotCompileException(e); - } - } - - private static String javaName(CtClass clazz) { - return Descriptor.toJavaName(Descriptor.toJvmName(clazz)); - } - - private static String javaName(String name) { - return Descriptor.toJavaName(Descriptor.toJvmName(name)); - } -} diff --git a/src/com/wenshuo/agent/javassist/Translator.java b/src/com/wenshuo/agent/javassist/Translator.java deleted file mode 100644 index 1492f31..0000000 --- a/src/com/wenshuo/agent/javassist/Translator.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -/** - * An observer of Loader. - * The users can define a class implementing this - * interface and attach an instance of that class to a - * Loader object so that it can translate a class file - * when the class file is loaded into the JVM. - * - * @see Loader#addTranslator(ClassPool, Translator) - */ -public interface Translator { - /** - * Is invoked by a Loader for initialization - * when the object is attached to the Loader object. - * This method can be used for getting (for caching) some - * CtClass objects that will be accessed - * in onLoad() in Translator. - * - * @param pool the ClassPool that this translator - * should use. - * @see Loader - * @throws NotFoundException if a CtClass cannot be found. - * @throws CannotCompileException if the initialization by this method - * fails. - */ - void start(ClassPool pool) - throws NotFoundException, CannotCompileException; - - /** - * Is invoked by a Loader for notifying that - * a class is loaded. The Loader calls - * - *

-     * pool.get(classname).toBytecode()
- * - * to read the class file after onLoad() returns. - * - *

classname may be the name of a class - * that has not been created yet. - * If so, onLoad() must create that class so that - * the Loader can read it after onLoad() - * returns. - * - * @param pool the ClassPool that this translator - * should use. - * @param classname the name of the class being loaded. - * @see Loader - * @throws NotFoundException if a CtClass cannot be found. - * @throws CannotCompileException if the code transformation - * by this method fails. - */ - void onLoad(ClassPool pool, String classname) - throws NotFoundException, CannotCompileException; -} diff --git a/src/com/wenshuo/agent/javassist/URLClassPath.java b/src/com/wenshuo/agent/javassist/URLClassPath.java deleted file mode 100644 index a66b87a..0000000 --- a/src/com/wenshuo/agent/javassist/URLClassPath.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist; - -import java.io.*; -import java.net.*; - -/** - * A class search-path specified with URL (http). - * - * @see javassist.ClassPath - * @see ClassPool#insertClassPath(ClassPath) - * @see ClassPool#appendClassPath(ClassPath) - */ -public class URLClassPath implements ClassPath { - protected String hostname; - protected int port; - protected String directory; - protected String packageName; - - /** - * Creates a search path specified with URL (http). - * - *

This search path is used only if a requested - * class name starts with the name specified by packageName. - * If packageName is "org.javassist." and a requested class is - * "org.javassist.test.Main", then the given URL is used for loading that class. - * The URLClassPath obtains a class file from: - * - *

http://www.javassist.org:80/java/classes/org/javassist/test/Main.class
-     * 
- * - *

Here, we assume that host is "www.javassist.org", - * port is 80, and directory is "/java/classes/". - * - *

If packageName is null, the URL is used - * for loading any class. - * - * @param host host name - * @param port port number - * @param directory directory name ending with "/". - * It can be "/" (root directory). - * It must start with "/". - * @param packageName package name. It must end with "." (dot). - */ - public URLClassPath(String host, int port, - String directory, String packageName) { - hostname = host; - this.port = port; - this.directory = directory; - this.packageName = packageName; - } - - public String toString() { - return hostname + ":" + port + directory; - } - - /** - * Opens a class file with http. - * - * @return null if the class file could not be found. - */ - public InputStream openClassfile(String classname) { - try { - URLConnection con = openClassfile0(classname); - if (con != null) - return con.getInputStream(); - } - catch (IOException e) {} - return null; // not found - } - - private URLConnection openClassfile0(String classname) throws IOException { - if (packageName == null || classname.startsWith(packageName)) { - String jarname - = directory + classname.replace('.', '/') + ".class"; - return fetchClass0(hostname, port, jarname); - } - else - return null; // not found - } - - /** - * Returns the URL. - * - * @return null if the class file could not be obtained. - */ - public URL find(String classname) { - try { - URLConnection con = openClassfile0(classname); - InputStream is = con.getInputStream(); - if (is != null) { - is.close(); - return con.getURL(); - } - } - catch (IOException e) {} - return null; - } - - /** - * Closes this class path. - */ - public void close() {} - - /** - * Reads a class file on an http server. - * - * @param host host name - * @param port port number - * @param directory directory name ending with "/". - * It can be "/" (root directory). - * It must start with "/". - * @param classname fully-qualified class name - */ - public static byte[] fetchClass(String host, int port, - String directory, String classname) - throws IOException - { - byte[] b; - URLConnection con = fetchClass0(host, port, - directory + classname.replace('.', '/') + ".class"); - int size = con.getContentLength(); - InputStream s = con.getInputStream(); - try { - if (size <= 0) - b = ClassPoolTail.readStream(s); - else { - b = new byte[size]; - int len = 0; - do { - int n = s.read(b, len, size - len); - if (n < 0) - throw new IOException("the stream was closed: " - + classname); - - len += n; - } while (len < size); - } - } - finally { - s.close(); - } - - return b; - } - - private static URLConnection fetchClass0(String host, int port, - String filename) - throws IOException - { - URL url; - try { - url = new URL("http", host, port, filename); - } - catch (MalformedURLException e) { - // should never reache here. - throw new IOException("invalid URL?"); - } - - URLConnection con = url.openConnection(); - con.connect(); - return con; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/AccessFlag.java b/src/com/wenshuo/agent/javassist/bytecode/AccessFlag.java deleted file mode 100644 index e550e23..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/AccessFlag.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -/** - * A support class providing static methods and constants - * for access modifiers such as public, private, ... - */ -public class AccessFlag { - public static final int PUBLIC = 0x0001; - public static final int PRIVATE = 0x0002; - public static final int PROTECTED = 0x0004; - public static final int STATIC = 0x0008; - public static final int FINAL = 0x0010; - public static final int SYNCHRONIZED = 0x0020; - public static final int VOLATILE = 0x0040; - public static final int BRIDGE = 0x0040; // for method_info - public static final int TRANSIENT = 0x0080; - public static final int VARARGS = 0x0080; // for method_info - public static final int NATIVE = 0x0100; - public static final int INTERFACE = 0x0200; - public static final int ABSTRACT = 0x0400; - public static final int STRICT = 0x0800; - public static final int SYNTHETIC = 0x1000; - public static final int ANNOTATION = 0x2000; - public static final int ENUM = 0x4000; - public static final int MANDATED = 0x8000; - - public static final int SUPER = 0x0020; - - // Note: 0x0020 is assigned to both ACC_SUPER and ACC_SYNCHRONIZED - // although java.lang.reflect.Modifier does not recognize ACC_SUPER. - - /** - * Truns the public bit on. The protected and private bits are - * cleared. - */ - public static int setPublic(int accflags) { - return (accflags & ~(PRIVATE | PROTECTED)) | PUBLIC; - } - - /** - * Truns the protected bit on. The protected and public bits are - * cleared. - */ - public static int setProtected(int accflags) { - return (accflags & ~(PRIVATE | PUBLIC)) | PROTECTED; - } - - /** - * Truns the private bit on. The protected and private bits are - * cleared. - */ - public static int setPrivate(int accflags) { - return (accflags & ~(PROTECTED | PUBLIC)) | PRIVATE; - } - - /** - * Clears the public, protected, and private bits. - */ - public static int setPackage(int accflags) { - return (accflags & ~(PROTECTED | PUBLIC | PRIVATE)); - } - - /** - * Returns true if the access flags include the public bit. - */ - public static boolean isPublic(int accflags) { - return (accflags & PUBLIC) != 0; - } - - /** - * Returns true if the access flags include the protected bit. - */ - public static boolean isProtected(int accflags) { - return (accflags & PROTECTED) != 0; - } - - /** - * Returns true if the access flags include the private bit. - */ - public static boolean isPrivate(int accflags) { - return (accflags & PRIVATE) != 0; - } - - /** - * Returns true if the access flags include neither public, protected, - * or private. - */ - public static boolean isPackage(int accflags) { - return (accflags & (PROTECTED | PUBLIC | PRIVATE)) == 0; - } - - /** - * Clears a specified bit in accflags. - */ - public static int clear(int accflags, int clearBit) { - return accflags & ~clearBit; - } - - /** - * Converts a javassist.Modifier into - * a javassist.bytecode.AccessFlag. - * - * @param modifier javassist.Modifier - */ - public static int of(int modifier) { - return modifier; - } - - /** - * Converts a javassist.bytecode.AccessFlag - * into a javassist.Modifier. - * - * @param accflags javassist.bytecode.Accessflag - */ - public static int toModifier(int accflags) { - return accflags; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java deleted file mode 100644 index 4bbddf3..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.bytecode.annotation.AnnotationsWriter; -import com.wenshuo.agent.javassist.bytecode.annotation.MemberValue; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -/** - * A class representing AnnotationDefault_attribute. - * - *

For example, if you declare the following annotation type: - * - *

- * @interface Author {
- *   String name() default "Shakespeare";
- *   int age() default 99;
- * }
- * 
- * - *

The defautl values of name and age - * are stored as annotation default attributes in Author.class. - * The following code snippet obtains the default value of name: - * - *

- * ClassPool pool = ...
- * CtClass cc = pool.get("Author");
- * CtMethod cm = cc.getDeclaredMethod("age");
- * MethodInfo minfo = cm.getMethodInfo();
- * AnnotationDefaultAttribute ada
- *         = (AnnotationDefaultAttribute)
- *           minfo.getAttribute(AnnotationDefaultAttribute.tag);
- * MemberValue value = ada.getDefaultValue());    // default value of age
- * 
- * - *

If the following statement is executed after the code above, - * the default value of age is set to 80: - * - *

- * ada.setDefaultValue(new IntegerMemberValue(minfo.getConstPool(), 80));
- * 
- * - * @see AnnotationsAttribute - * @see javassist.bytecode.annotation.MemberValue - */ - -public class AnnotationDefaultAttribute extends AttributeInfo { - /** - * The name of the AnnotationDefault attribute. - */ - public static final String tag = "AnnotationDefault"; - - /** - * Constructs an AnnotationDefault_attribute. - * - * @param cp constant pool - * @param info the contents of this attribute. It does not - * include attribute_name_index or - * attribute_length. - */ - public AnnotationDefaultAttribute(ConstPool cp, byte[] info) { - super(cp, tag, info); - } - - /** - * Constructs an empty AnnotationDefault_attribute. - * The default value can be set by setDefaultValue(). - * - * @param cp constant pool - * @see #setDefaultValue(javassist.bytecode.annotation.MemberValue) - */ - public AnnotationDefaultAttribute(ConstPool cp) { - this(cp, new byte[] { 0, 0 }); - } - - /** - * @param n the attribute name. - */ - AnnotationDefaultAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Copies this attribute and returns a new copy. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - AnnotationsAttribute.Copier copier - = new AnnotationsAttribute.Copier(info, constPool, newCp, classnames); - try { - copier.memberValue(0); - return new AnnotationDefaultAttribute(newCp, copier.close()); - } - catch (Exception e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Obtains the default value represented by this attribute. - */ - public MemberValue getDefaultValue() - { - try { - return new AnnotationsAttribute.Parser(info, constPool) - .parseMemberValue(); - } - catch (Exception e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Changes the default value represented by this attribute. - * - * @param value the new value. - * @see javassist.bytecode.annotation.Annotation#createMemberValue(ConstPool, CtClass) - */ - public void setDefaultValue(MemberValue value) { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - AnnotationsWriter writer = new AnnotationsWriter(output, constPool); - try { - value.write(writer); - writer.close(); - } - catch (IOException e) { - throw new RuntimeException(e); // should never reach here. - } - - set(output.toByteArray()); - - } - - /** - * Returns a string representation of this object. - */ - public String toString() { - return getDefaultValue().toString(); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java deleted file mode 100644 index 59fa6a4..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.util.Map; -import java.util.HashMap; -import java.io.IOException; -import java.io.DataInputStream; -import java.io.ByteArrayOutputStream; -import com.wenshuo.agent.javassist.bytecode.annotation.*; - -/** - * A class representing - * RuntimeVisibleAnnotations_attribute and - * RuntimeInvisibleAnnotations_attribute. - * - *

To obtain an AnnotationAttribute object, invoke - * getAttribute(AnnotationsAttribute.visibleTag) - * in ClassFile, MethodInfo, - * or FieldInfo. The obtained attribute is a - * runtime visible annotations attribute. - * If the parameter is - * AnnotationAttribute.invisibleTag, then the obtained - * attribute is a runtime invisible one. - * - *

For example, - * - *

- * import com.wenshuo.agent.javassist.bytecode.annotation.Annotation;
- *    :
- * CtMethod m = ... ;
- * MethodInfo minfo = m.getMethodInfo();
- * AnnotationsAttribute attr = (AnnotationsAttribute)
- *         minfo.getAttribute(AnnotationsAttribute.invisibleTag);
- * Annotation an = attr.getAnnotation("Author");
- * String s = ((StringMemberValue)an.getMemberValue("name")).getValue();
- * System.out.println("@Author(name=" + s + ")");
- * 
- * - *

This code snippet retrieves an annotation of the type Author - * from the MethodInfo object specified by minfo. - * Then, it prints the value of name in Author. - * - *

If the annotation type Author is annotated by a meta annotation: - * - *

- * @Retention(RetentionPolicy.RUNTIME)
- * 
- * - *

Then Author is visible at runtime. Therefore, the third - * statement of the code snippet above must be changed into: - * - *

- * AnnotationsAttribute attr = (AnnotationsAttribute)
- *         minfo.getAttribute(AnnotationsAttribute.visibleTag);
- * 
- * - *

The attribute tag must be visibleTag instead of - * invisibleTag. - * - *

If the member value of an annotation is not specified, the default value - * is used as that member value. If so, getMemberValue() in - * Annotation returns null - * since the default value is not included in the - * AnnotationsAttribute. It is included in the - * AnnotationDefaultAttribute of the method declared in the - * annotation type. - * - *

If you want to record a new AnnotationAttribute object, execute the - * following snippet: - * - *

- * ClassFile cf = ... ;
- * ConstPool cp = cf.getConstPool();
- * AnnotationsAttribute attr
- *     = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
- * Annotation a = new Annotation("Author", cp);
- * a.addMemberValue("name", new StringMemberValue("Chiba", cp));
- * attr.setAnnotation(a);
- * cf.addAttribute(attr);
- * cf.setVersionToJava5();
- * 
- * - *

The last statement is necessary if the class file was produced by - * javac of JDK 1.4 or earlier. Otherwise, it is not necessary. - * - * @see AnnotationDefaultAttribute - * @see javassist.bytecode.annotation.Annotation - */ -public class AnnotationsAttribute extends AttributeInfo { - /** - * The name of the RuntimeVisibleAnnotations attribute. - */ - public static final String visibleTag = "RuntimeVisibleAnnotations"; - - /** - * The name of the RuntimeInvisibleAnnotations attribute. - */ - public static final String invisibleTag = "RuntimeInvisibleAnnotations"; - - /** - * Constructs a Runtime(In)VisibleAnnotations_attribute. - * - * @param cp constant pool - * @param attrname attribute name (visibleTag or - * invisibleTag). - * @param info the contents of this attribute. It does not - * include attribute_name_index or - * attribute_length. - */ - public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) { - super(cp, attrname, info); - } - - /** - * Constructs an empty - * Runtime(In)VisibleAnnotations_attribute. - * A new annotation can be later added to the created attribute - * by setAnnotations(). - * - * @param cp constant pool - * @param attrname attribute name (visibleTag or - * invisibleTag). - * @see #setAnnotations(Annotation[]) - */ - public AnnotationsAttribute(ConstPool cp, String attrname) { - this(cp, attrname, new byte[] { 0, 0 }); - } - - /** - * @param n the attribute name. - */ - AnnotationsAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Returns num_annotations. - */ - public int numAnnotations() { - return ByteArray.readU16bit(info, 0); - } - - /** - * Copies this attribute and returns a new copy. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - Copier copier = new Copier(info, constPool, newCp, classnames); - try { - copier.annotationArray(); - return new AnnotationsAttribute(newCp, getName(), copier.close()); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Parses the annotations and returns a data structure representing - * the annotation with the specified type. See also - * getAnnotations() as to the returned data structure. - * - * @param type the annotation type. - * @return null if the specified annotation type is not included. - * @see #getAnnotations() - */ - public Annotation getAnnotation(String type) { - Annotation[] annotations = getAnnotations(); - for (int i = 0; i < annotations.length; i++) { - if (annotations[i].getTypeName().equals(type)) - return annotations[i]; - } - - return null; - } - - /** - * Adds an annotation. If there is an annotation with the same type, - * it is removed before the new annotation is added. - * - * @param annotation the added annotation. - */ - public void addAnnotation(Annotation annotation) { - String type = annotation.getTypeName(); - Annotation[] annotations = getAnnotations(); - for (int i = 0; i < annotations.length; i++) { - if (annotations[i].getTypeName().equals(type)) { - annotations[i] = annotation; - setAnnotations(annotations); - return; - } - } - - Annotation[] newlist = new Annotation[annotations.length + 1]; - System.arraycopy(annotations, 0, newlist, 0, annotations.length); - newlist[annotations.length] = annotation; - setAnnotations(newlist); - } - - /** - * Parses the annotations and returns a data structure representing - * that parsed annotations. Note that changes of the node values of the - * returned tree are not reflected on the annotations represented by - * this object unless the tree is copied back to this object by - * setAnnotations(). - * - * @see #setAnnotations(Annotation[]) - */ - public Annotation[] getAnnotations() { - try { - return new Parser(info, constPool).parseAnnotations(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Changes the annotations represented by this object according to - * the given array of Annotation objects. - * - * @param annotations the data structure representing the - * new annotations. - */ - public void setAnnotations(Annotation[] annotations) { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - AnnotationsWriter writer = new AnnotationsWriter(output, constPool); - try { - int n = annotations.length; - writer.numAnnotations(n); - for (int i = 0; i < n; ++i) - annotations[i].write(writer); - - writer.close(); - } - catch (IOException e) { - throw new RuntimeException(e); // should never reach here. - } - - set(output.toByteArray()); - } - - /** - * Changes the annotations. A call to this method is equivalent to: - *

setAnnotations(new Annotation[] { annotation })
- * - * @param annotation the data structure representing - * the new annotation. - */ - public void setAnnotation(Annotation annotation) { - setAnnotations(new Annotation[] { annotation }); - } - - /** - * @param oldname a JVM class name. - * @param newname a JVM class name. - */ - void renameClass(String oldname, String newname) { - HashMap map = new HashMap(); - map.put(oldname, newname); - renameClass(map); - } - - void renameClass(Map classnames) { - Renamer renamer = new Renamer(info, getConstPool(), classnames); - try { - renamer.annotationArray(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - void getRefClasses(Map classnames) { renameClass(classnames); } - - /** - * Returns a string representation of this object. - */ - public String toString() { - Annotation[] a = getAnnotations(); - StringBuilder sbuf = new StringBuilder(); - int i = 0; - while (i < a.length) { - sbuf.append(a[i++].toString()); - if (i != a.length) - sbuf.append(", "); - } - - return sbuf.toString(); - } - - static class Walker { - byte[] info; - - Walker(byte[] attrInfo) { - info = attrInfo; - } - - final void parameters() throws Exception { - int numParam = info[0] & 0xff; - parameters(numParam, 1); - } - - void parameters(int numParam, int pos) throws Exception { - for (int i = 0; i < numParam; ++i) - pos = annotationArray(pos); - } - - final void annotationArray() throws Exception { - annotationArray(0); - } - - final int annotationArray(int pos) throws Exception { - int num = ByteArray.readU16bit(info, pos); - return annotationArray(pos + 2, num); - } - - int annotationArray(int pos, int num) throws Exception { - for (int i = 0; i < num; ++i) - pos = annotation(pos); - - return pos; - } - - final int annotation(int pos) throws Exception { - int type = ByteArray.readU16bit(info, pos); - int numPairs = ByteArray.readU16bit(info, pos + 2); - return annotation(pos + 4, type, numPairs); - } - - int annotation(int pos, int type, int numPairs) throws Exception { - for (int j = 0; j < numPairs; ++j) - pos = memberValuePair(pos); - - return pos; - } - - /** - * {@code element_value_paris} - */ - final int memberValuePair(int pos) throws Exception { - int nameIndex = ByteArray.readU16bit(info, pos); - return memberValuePair(pos + 2, nameIndex); - } - - /** - * {@code element_value_paris[]} - */ - int memberValuePair(int pos, int nameIndex) throws Exception { - return memberValue(pos); - } - - /** - * {@code element_value} - */ - final int memberValue(int pos) throws Exception { - int tag = info[pos] & 0xff; - if (tag == 'e') { - int typeNameIndex = ByteArray.readU16bit(info, pos + 1); - int constNameIndex = ByteArray.readU16bit(info, pos + 3); - enumMemberValue(pos, typeNameIndex, constNameIndex); - return pos + 5; - } - else if (tag == 'c') { - int index = ByteArray.readU16bit(info, pos + 1); - classMemberValue(pos, index); - return pos + 3; - } - else if (tag == '@') - return annotationMemberValue(pos + 1); - else if (tag == '[') { - int num = ByteArray.readU16bit(info, pos + 1); - return arrayMemberValue(pos + 3, num); - } - else { // primitive types or String. - int index = ByteArray.readU16bit(info, pos + 1); - constValueMember(tag, index); - return pos + 3; - } - } - - /** - * {@code const_value_index} - */ - void constValueMember(int tag, int index) throws Exception {} - - /** - * {@code enum_const_value} - */ - void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) - throws Exception { - } - - /** - * {@code class_info_index} - */ - void classMemberValue(int pos, int index) throws Exception {} - - /** - * {@code annotation_value} - */ - int annotationMemberValue(int pos) throws Exception { - return annotation(pos); - } - - /** - * {@code array_value} - */ - int arrayMemberValue(int pos, int num) throws Exception { - for (int i = 0; i < num; ++i) { - pos = memberValue(pos); - } - - return pos; - } - } - - static class Renamer extends Walker { - ConstPool cpool; - Map classnames; - - /** - * Constructs a renamer. It renames some class names - * into the new names specified by map. - * - * @param info the annotations attribute. - * @param cp the constant pool. - * @param map pairs of replaced and substituted class names. - * It can be null. - */ - Renamer(byte[] info, ConstPool cp, Map map) { - super(info); - cpool = cp; - classnames = map; - } - - int annotation(int pos, int type, int numPairs) throws Exception { - renameType(pos - 4, type); - return super.annotation(pos, type, numPairs); - } - - void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) - throws Exception - { - renameType(pos + 1, typeNameIndex); - super.enumMemberValue(pos, typeNameIndex, constNameIndex); - } - - void classMemberValue(int pos, int index) throws Exception { - renameType(pos + 1, index); - super.classMemberValue(pos, index); - } - - private void renameType(int pos, int index) { - String name = cpool.getUtf8Info(index); - String newName = Descriptor.rename(name, classnames); - if (!name.equals(newName)) { - int index2 = cpool.addUtf8Info(newName); - ByteArray.write16bit(index2, info, pos); - } - } - } - - static class Copier extends Walker { - ByteArrayOutputStream output; - AnnotationsWriter writer; - ConstPool srcPool, destPool; - Map classnames; - - /** - * Constructs a copier. This copier renames some class names - * into the new names specified by map when it copies - * an annotation attribute. - * - * @param info the source attribute. - * @param src the constant pool of the source class. - * @param dest the constant pool of the destination class. - * @param map pairs of replaced and substituted class names. - * It can be null. - */ - Copier(byte[] info, ConstPool src, ConstPool dest, Map map) { - this(info, src, dest, map, true); - } - - Copier(byte[] info, ConstPool src, ConstPool dest, Map map, boolean makeWriter) { - super(info); - output = new ByteArrayOutputStream(); - if (makeWriter) - writer = new AnnotationsWriter(output, dest); - - srcPool = src; - destPool = dest; - classnames = map; - } - - byte[] close() throws IOException { - writer.close(); - return output.toByteArray(); - } - - void parameters(int numParam, int pos) throws Exception { - writer.numParameters(numParam); - super.parameters(numParam, pos); - } - - int annotationArray(int pos, int num) throws Exception { - writer.numAnnotations(num); - return super.annotationArray(pos, num); - } - - int annotation(int pos, int type, int numPairs) throws Exception { - writer.annotation(copyType(type), numPairs); - return super.annotation(pos, type, numPairs); - } - - int memberValuePair(int pos, int nameIndex) throws Exception { - writer.memberValuePair(copy(nameIndex)); - return super.memberValuePair(pos, nameIndex); - } - - void constValueMember(int tag, int index) throws Exception { - writer.constValueIndex(tag, copy(index)); - super.constValueMember(tag, index); - } - - void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) - throws Exception - { - writer.enumConstValue(copyType(typeNameIndex), copy(constNameIndex)); - super.enumMemberValue(pos, typeNameIndex, constNameIndex); - } - - void classMemberValue(int pos, int index) throws Exception { - writer.classInfoIndex(copyType(index)); - super.classMemberValue(pos, index); - } - - int annotationMemberValue(int pos) throws Exception { - writer.annotationValue(); - return super.annotationMemberValue(pos); - } - - int arrayMemberValue(int pos, int num) throws Exception { - writer.arrayValue(num); - return super.arrayMemberValue(pos, num); - } - - /** - * Copies a constant pool entry into the destination constant pool - * and returns the index of the copied entry. - * - * @param srcIndex the index of the copied entry into the source - * constant pool. - * @return the index of the copied item into the destination - * constant pool. - */ - int copy(int srcIndex) { - return srcPool.copy(srcIndex, destPool, classnames); - } - - /** - * Copies a constant pool entry into the destination constant pool - * and returns the index of the copied entry. That entry must be - * a Utf8Info representing a class name in the L; form. - * - * @param srcIndex the index of the copied entry into the source - * constant pool. - * @return the index of the copied item into the destination - * constant pool. - */ - int copyType(int srcIndex) { - String name = srcPool.getUtf8Info(srcIndex); - String newName = Descriptor.rename(name, classnames); - return destPool.addUtf8Info(newName); - } - } - - static class Parser extends Walker { - ConstPool pool; - Annotation[][] allParams; // all parameters - Annotation[] allAnno; // all annotations - Annotation currentAnno; // current annotation - MemberValue currentMember; // current member - - /** - * Constructs a parser. This parser constructs a parse tree of - * the annotations. - * - * @param info the attribute. - * @param src the constant pool. - */ - Parser(byte[] info, ConstPool cp) { - super(info); - pool = cp; - } - - Annotation[][] parseParameters() throws Exception { - parameters(); - return allParams; - } - - Annotation[] parseAnnotations() throws Exception { - annotationArray(); - return allAnno; - } - - MemberValue parseMemberValue() throws Exception { - memberValue(0); - return currentMember; - } - - void parameters(int numParam, int pos) throws Exception { - Annotation[][] params = new Annotation[numParam][]; - for (int i = 0; i < numParam; ++i) { - pos = annotationArray(pos); - params[i] = allAnno; - } - - allParams = params; - } - - int annotationArray(int pos, int num) throws Exception { - Annotation[] array = new Annotation[num]; - for (int i = 0; i < num; ++i) { - pos = annotation(pos); - array[i] = currentAnno; - } - - allAnno = array; - return pos; - } - - int annotation(int pos, int type, int numPairs) throws Exception { - currentAnno = new Annotation(type, pool); - return super.annotation(pos, type, numPairs); - } - - int memberValuePair(int pos, int nameIndex) throws Exception { - pos = super.memberValuePair(pos, nameIndex); - currentAnno.addMemberValue(nameIndex, currentMember); - return pos; - } - - void constValueMember(int tag, int index) throws Exception { - MemberValue m; - ConstPool cp = pool; - switch (tag) { - case 'B' : - m = new ByteMemberValue(index, cp); - break; - case 'C' : - m = new CharMemberValue(index, cp); - break; - case 'D' : - m = new DoubleMemberValue(index, cp); - break; - case 'F' : - m = new FloatMemberValue(index, cp); - break; - case 'I' : - m = new IntegerMemberValue(index, cp); - break; - case 'J' : - m = new LongMemberValue(index, cp); - break; - case 'S' : - m = new ShortMemberValue(index, cp); - break; - case 'Z' : - m = new BooleanMemberValue(index, cp); - break; - case 's' : - m = new StringMemberValue(index, cp); - break; - default : - throw new RuntimeException("unknown tag:" + tag); - } - - currentMember = m; - super.constValueMember(tag, index); - } - - void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) - throws Exception - { - currentMember = new EnumMemberValue(typeNameIndex, - constNameIndex, pool); - super.enumMemberValue(pos, typeNameIndex, constNameIndex); - } - - void classMemberValue(int pos, int index) throws Exception { - currentMember = new ClassMemberValue(index, pool); - super.classMemberValue(pos, index); - } - - int annotationMemberValue(int pos) throws Exception { - Annotation anno = currentAnno; - pos = super.annotationMemberValue(pos); - currentMember = new AnnotationMemberValue(currentAnno, pool); - currentAnno = anno; - return pos; - } - - int arrayMemberValue(int pos, int num) throws Exception { - ArrayMemberValue amv = new ArrayMemberValue(pool); - MemberValue[] elements = new MemberValue[num]; - for (int i = 0; i < num; ++i) { - pos = memberValue(pos); - elements[i] = currentMember; - } - - amv.setValue(elements); - currentMember = amv; - return pos; - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java b/src/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java deleted file mode 100644 index b8f799a..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Map; -import java.util.ArrayList; -import java.util.ListIterator; -import java.util.List; -import java.util.Iterator; - -// Note: if you define a new subclass of AttributeInfo, then -// update AttributeInfo.read(), .copy(), and (maybe) write(). - -/** - * attribute_info structure. - */ -public class AttributeInfo { - protected ConstPool constPool; - int name; - byte[] info; - - protected AttributeInfo(ConstPool cp, int attrname, byte[] attrinfo) { - constPool = cp; - name = attrname; - info = attrinfo; - } - - protected AttributeInfo(ConstPool cp, String attrname) { - this(cp, attrname, (byte[])null); - } - - /** - * Constructs an attribute_info structure. - * - * @param cp constant pool table - * @param attrname attribute name - * @param attrinfo info field - * of attribute_info structure. - */ - public AttributeInfo(ConstPool cp, String attrname, byte[] attrinfo) { - this(cp, cp.addUtf8Info(attrname), attrinfo); - } - - protected AttributeInfo(ConstPool cp, int n, DataInputStream in) - throws IOException - { - constPool = cp; - name = n; - int len = in.readInt(); - info = new byte[len]; - if (len > 0) - in.readFully(info); - } - - static AttributeInfo read(ConstPool cp, DataInputStream in) - throws IOException - { - int name = in.readUnsignedShort(); - String nameStr = cp.getUtf8Info(name); - char first = nameStr.charAt(0); - if (first < 'M') { - if (first < 'E') { - if (nameStr.equals(AnnotationDefaultAttribute.tag)) - return new AnnotationDefaultAttribute(cp, name, in); - else if (nameStr.equals(BootstrapMethodsAttribute.tag)) - return new BootstrapMethodsAttribute(cp, name, in); - else if (nameStr.equals(CodeAttribute.tag)) - return new CodeAttribute(cp, name, in); - else if (nameStr.equals(ConstantAttribute.tag)) - return new ConstantAttribute(cp, name, in); - else if (nameStr.equals(DeprecatedAttribute.tag)) - return new DeprecatedAttribute(cp, name, in); - } - else { - if (nameStr.equals(EnclosingMethodAttribute.tag)) - return new EnclosingMethodAttribute(cp, name, in); - else if (nameStr.equals(ExceptionsAttribute.tag)) - return new ExceptionsAttribute(cp, name, in); - else if (nameStr.equals(InnerClassesAttribute.tag)) - return new InnerClassesAttribute(cp, name, in); - else if (nameStr.equals(LineNumberAttribute.tag)) - return new LineNumberAttribute(cp, name, in); - else if (nameStr.equals(LocalVariableAttribute.tag)) - return new LocalVariableAttribute(cp, name, in); - else if (nameStr.equals(LocalVariableTypeAttribute.tag)) - return new LocalVariableTypeAttribute(cp, name, in); - } - } - else { - if (first < 'S') { - /* Note that the names of Annotations attributes begin with 'R'. - */ - if (nameStr.equals(MethodParametersAttribute.tag)) - return new MethodParametersAttribute(cp, name, in); - else if (nameStr.equals(AnnotationsAttribute.visibleTag) - || nameStr.equals(AnnotationsAttribute.invisibleTag)) { - // RuntimeVisibleAnnotations or RuntimeInvisibleAnnotations - return new AnnotationsAttribute(cp, name, in); - } - else if (nameStr.equals(ParameterAnnotationsAttribute.visibleTag) - || nameStr.equals(ParameterAnnotationsAttribute.invisibleTag)) - return new ParameterAnnotationsAttribute(cp, name, in); - else if (nameStr.equals(TypeAnnotationsAttribute.visibleTag) - || nameStr.equals(TypeAnnotationsAttribute.invisibleTag)) - return new TypeAnnotationsAttribute(cp, name, in); - } - else { - if (nameStr.equals(SignatureAttribute.tag)) - return new SignatureAttribute(cp, name, in); - else if (nameStr.equals(SourceFileAttribute.tag)) - return new SourceFileAttribute(cp, name, in); - else if (nameStr.equals(SyntheticAttribute.tag)) - return new SyntheticAttribute(cp, name, in); - else if (nameStr.equals(StackMap.tag)) - return new StackMap(cp, name, in); - else if (nameStr.equals(StackMapTable.tag)) - return new StackMapTable(cp, name, in); - } - } - - return new AttributeInfo(cp, name, in); - } - - /** - * Returns an attribute name. - */ - public String getName() { - return constPool.getUtf8Info(name); - } - - /** - * Returns a constant pool table. - */ - public ConstPool getConstPool() { return constPool; } - - /** - * Returns the length of this attribute_info - * structure. - * The returned value is attribute_length + 6. - */ - public int length() { - return info.length + 6; - } - - /** - * Returns the info field - * of this attribute_info structure. - * - *

This method is not available if the object is an instance - * of CodeAttribute. - */ - public byte[] get() { return info; } - - /** - * Sets the info field - * of this attribute_info structure. - * - *

This method is not available if the object is an instance - * of CodeAttribute. - */ - public void set(byte[] newinfo) { info = newinfo; } - - /** - * Makes a copy. Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - int s = info.length; - byte[] srcInfo = info; - byte[] newInfo = new byte[s]; - for (int i = 0; i < s; ++i) - newInfo[i] = srcInfo[i]; - - return new AttributeInfo(newCp, getName(), newInfo); - } - - void write(DataOutputStream out) throws IOException { - out.writeShort(name); - out.writeInt(info.length); - if (info.length > 0) - out.write(info); - } - - static int getLength(ArrayList list) { - int size = 0; - int n = list.size(); - for (int i = 0; i < n; ++i) { - AttributeInfo attr = (AttributeInfo)list.get(i); - size += attr.length(); - } - - return size; - } - - static AttributeInfo lookup(ArrayList list, String name) { - if (list == null) - return null; - - ListIterator iterator = list.listIterator(); - while (iterator.hasNext()) { - AttributeInfo ai = (AttributeInfo)iterator.next(); - if (ai.getName().equals(name)) - return ai; - } - - return null; // no such attribute - } - - static synchronized void remove(ArrayList list, String name) { - if (list == null) - return; - - ListIterator iterator = list.listIterator(); - while (iterator.hasNext()) { - AttributeInfo ai = (AttributeInfo)iterator.next(); - if (ai.getName().equals(name)) - iterator.remove(); - } - } - - static void writeAll(ArrayList list, DataOutputStream out) - throws IOException - { - if (list == null) - return; - - int n = list.size(); - for (int i = 0; i < n; ++i) { - AttributeInfo attr = (AttributeInfo)list.get(i); - attr.write(out); - } - } - - static ArrayList copyAll(ArrayList list, ConstPool cp) { - if (list == null) - return null; - - ArrayList newList = new ArrayList(); - int n = list.size(); - for (int i = 0; i < n; ++i) { - AttributeInfo attr = (AttributeInfo)list.get(i); - newList.add(attr.copy(cp, null)); - } - - return newList; - } - - /* The following two methods are used to implement - * ClassFile.renameClass(). - * Only CodeAttribute, LocalVariableAttribute, - * AnnotationsAttribute, and SignatureAttribute - * override these methods. - */ - void renameClass(String oldname, String newname) {} - void renameClass(Map classnames) {} - - static void renameClass(List attributes, String oldname, String newname) { - Iterator iterator = attributes.iterator(); - while (iterator.hasNext()) { - AttributeInfo ai = (AttributeInfo)iterator.next(); - ai.renameClass(oldname, newname); - } - } - - static void renameClass(List attributes, Map classnames) { - Iterator iterator = attributes.iterator(); - while (iterator.hasNext()) { - AttributeInfo ai = (AttributeInfo)iterator.next(); - ai.renameClass(classnames); - } - } - - void getRefClasses(Map classnames) {} - - static void getRefClasses(List attributes, Map classnames) { - Iterator iterator = attributes.iterator(); - while (iterator.hasNext()) { - AttributeInfo ai = (AttributeInfo)iterator.next(); - ai.getRefClasses(classnames); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/BadBytecode.java b/src/com/wenshuo/agent/javassist/bytecode/BadBytecode.java deleted file mode 100644 index 6aeafc7..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/BadBytecode.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -/** - * Signals that a bad bytecode sequence has been found. - */ -public class BadBytecode extends Exception { - public BadBytecode(int opcode) { - super("bytecode " + opcode); - } - - public BadBytecode(String msg) { - super(msg); - } - - public BadBytecode(String msg, Throwable cause) { - super(msg, cause); - } - - public BadBytecode(MethodInfo minfo, Throwable cause) { - super(minfo.toString() + " in " - + minfo.getConstPool().getClassName() - + ": " + cause.getMessage(), cause); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java deleted file mode 100644 index 194c431..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -public class BootstrapMethodsAttribute extends AttributeInfo { - /** - * The name of this attribute "BootstrapMethods". - */ - public static final String tag = "BootstrapMethods"; - - /** - * An element of bootstrap_methods. - */ - public static class BootstrapMethod { - /** - * Constructs an element of bootstrap_methods. - * - * @param method bootstrap_method_ref. - * @param args bootstrap_arguments. - */ - public BootstrapMethod(int method, int[] args) { - methodRef = method; - arguments = args; - } - - /** - * bootstrap_method_ref. - * The value at this index must be a CONSTANT_MethodHandle_info. - */ - public int methodRef; - - /** - * bootstrap_arguments. - */ - public int[] arguments; - } - - BootstrapMethodsAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Constructs a BootstrapMethods attribute. - * - * @param cp a constant pool table. - * @param methods the contents. - */ - public BootstrapMethodsAttribute(ConstPool cp, BootstrapMethod[] methods) { - super(cp, tag); - int size = 2; - for (int i = 0; i < methods.length; i++) - size += 4 + methods[i].arguments.length * 2; - - byte[] data = new byte[size]; - ByteArray.write16bit(methods.length, data, 0); // num_bootstrap_methods - int pos = 2; - for (int i = 0; i < methods.length; i++) { - ByteArray.write16bit(methods[i].methodRef, data, pos); - ByteArray.write16bit(methods[i].arguments.length, data, pos + 2); - int[] args = methods[i].arguments; - pos += 4; - for (int k = 0; k < args.length; k++) { - ByteArray.write16bit(args[k], data, pos); - pos += 2; - } - } - - set(data); - } - - /** - * Obtains bootstrap_methods in this attribute. - * - * @return an array of BootstrapMethod. Since it - * is a fresh copy, modifying the returned array does not - * affect the original contents of this attribute. - */ - public BootstrapMethod[] getMethods() { - byte[] data = this.get(); - int num = ByteArray.readU16bit(data, 0); - BootstrapMethod[] methods = new BootstrapMethod[num]; - int pos = 2; - for (int i = 0; i < num; i++) { - int ref = ByteArray.readU16bit(data, pos); - int len = ByteArray.readU16bit(data, pos + 2); - int[] args = new int[len]; - pos += 4; - for (int k = 0; k < len; k++) { - args[k] = ByteArray.readU16bit(data, pos); - pos += 2; - } - - methods[i] = new BootstrapMethod(ref, args); - } - - return methods; - } - - /** - * Makes a copy. Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - BootstrapMethod[] methods = getMethods(); - ConstPool thisCp = getConstPool(); - for (int i = 0; i < methods.length; i++) { - BootstrapMethod m = methods[i]; - m.methodRef = thisCp.copy(m.methodRef, newCp, classnames); - for (int k = 0; k < m.arguments.length; k++) - m.arguments[k] = thisCp.copy(m.arguments[k], newCp, classnames); - } - - return new BootstrapMethodsAttribute(newCp, methods); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ByteArray.java b/src/com/wenshuo/agent/javassist/bytecode/ByteArray.java deleted file mode 100644 index ad8fc3f..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ByteArray.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -/** - * A collection of static methods for reading and writing a byte array. - */ -public class ByteArray { - /** - * Reads an unsigned 16bit integer at the index. - */ - public static int readU16bit(byte[] code, int index) { - return ((code[index] & 0xff) << 8) | (code[index + 1] & 0xff); - } - - /** - * Reads a signed 16bit integer at the index. - */ - public static int readS16bit(byte[] code, int index) { - return (code[index] << 8) | (code[index + 1] & 0xff); - } - - /** - * Writes a 16bit integer at the index. - */ - public static void write16bit(int value, byte[] code, int index) { - code[index] = (byte)(value >>> 8); - code[index + 1] = (byte)value; - } - - /** - * Reads a 32bit integer at the index. - */ - public static int read32bit(byte[] code, int index) { - return (code[index] << 24) | ((code[index + 1] & 0xff) << 16) - | ((code[index + 2] & 0xff) << 8) | (code[index + 3] & 0xff); - } - - /** - * Writes a 32bit integer at the index. - */ - public static void write32bit(int value, byte[] code, int index) { - code[index] = (byte)(value >>> 24); - code[index + 1] = (byte)(value >>> 16); - code[index + 2] = (byte)(value >>> 8); - code[index + 3] = (byte)value; - } - - /** - * Copies a 32bit integer. - * - * @param src the source byte array. - * @param isrc the index into the source byte array. - * @param dest the destination byte array. - * @param idest the index into the destination byte array. - */ - static void copy32bit(byte[] src, int isrc, byte[] dest, int idest) { - dest[idest] = src[isrc]; - dest[idest + 1] = src[isrc + 1]; - dest[idest + 2] = src[isrc + 2]; - dest[idest + 3] = src[isrc + 3]; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ByteStream.java b/src/com/wenshuo/agent/javassist/bytecode/ByteStream.java deleted file mode 100644 index 7206ea7..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ByteStream.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.OutputStream; -import java.io.IOException; - -final class ByteStream extends OutputStream { - private byte[] buf; - private int count; - - public ByteStream() { this(32); } - - public ByteStream(int size) { - buf = new byte[size]; - count = 0; - } - - public int getPos() { return count; } - public int size() { return count; } - - public void writeBlank(int len) { - enlarge(len); - count += len; - } - - public void write(byte[] data) { - write(data, 0, data.length); - } - - public void write(byte[] data, int off, int len) { - enlarge(len); - System.arraycopy(data, off, buf, count, len); - count += len; - } - - public void write(int b) { - enlarge(1); - int oldCount = count; - buf[oldCount] = (byte)b; - count = oldCount + 1; - } - - public void writeShort(int s) { - enlarge(2); - int oldCount = count; - buf[oldCount] = (byte)(s >>> 8); - buf[oldCount + 1] = (byte)s; - count = oldCount + 2; - } - - public void writeInt(int i) { - enlarge(4); - int oldCount = count; - buf[oldCount] = (byte)(i >>> 24); - buf[oldCount + 1] = (byte)(i >>> 16); - buf[oldCount + 2] = (byte)(i >>> 8); - buf[oldCount + 3] = (byte)i; - count = oldCount + 4; - } - - public void writeLong(long i) { - enlarge(8); - int oldCount = count; - buf[oldCount] = (byte)(i >>> 56); - buf[oldCount + 1] = (byte)(i >>> 48); - buf[oldCount + 2] = (byte)(i >>> 40); - buf[oldCount + 3] = (byte)(i >>> 32); - buf[oldCount + 4] = (byte)(i >>> 24); - buf[oldCount + 5] = (byte)(i >>> 16); - buf[oldCount + 6] = (byte)(i >>> 8); - buf[oldCount + 7] = (byte)i; - count = oldCount + 8; - } - - public void writeFloat(float v) { - writeInt(Float.floatToIntBits(v)); - } - - public void writeDouble(double v) { - writeLong(Double.doubleToLongBits(v)); - } - - public void writeUTF(String s) { - int sLen = s.length(); - int pos = count; - enlarge(sLen + 2); - - byte[] buffer = buf; - buffer[pos++] = (byte)(sLen >>> 8); - buffer[pos++] = (byte)sLen; - for (int i = 0; i < sLen; ++i) { - char c = s.charAt(i); - if (0x01 <= c && c <= 0x7f) - buffer[pos++] = (byte)c; - else { - writeUTF2(s, sLen, i); - return; - } - } - - count = pos; - } - - private void writeUTF2(String s, int sLen, int offset) { - int size = sLen; - for (int i = offset; i < sLen; i++) { - int c = s.charAt(i); - if (c > 0x7ff) - size += 2; // 3 bytes code - else if (c == 0 || c > 0x7f) - ++size; // 2 bytes code - } - - if (size > 65535) - throw new RuntimeException( - "encoded string too long: " + sLen + size + " bytes"); - - enlarge(size + 2); - int pos = count; - byte[] buffer = buf; - buffer[pos] = (byte)(size >>> 8); - buffer[pos + 1] = (byte)size; - pos += 2 + offset; - for (int j = offset; j < sLen; ++j) { - int c = s.charAt(j); - if (0x01 <= c && c <= 0x7f) - buffer[pos++] = (byte) c; - else if (c > 0x07ff) { - buffer[pos] = (byte)(0xe0 | ((c >> 12) & 0x0f)); - buffer[pos + 1] = (byte)(0x80 | ((c >> 6) & 0x3f)); - buffer[pos + 2] = (byte)(0x80 | (c & 0x3f)); - pos += 3; - } - else { - buffer[pos] = (byte)(0xc0 | ((c >> 6) & 0x1f)); - buffer[pos + 1] = (byte)(0x80 | (c & 0x3f)); - pos += 2; - } - } - - count = pos; - } - - public void write(int pos, int value) { - buf[pos] = (byte)value; - } - - public void writeShort(int pos, int value) { - buf[pos] = (byte)(value >>> 8); - buf[pos + 1] = (byte)value; - } - - public void writeInt(int pos, int value) { - buf[pos] = (byte)(value >>> 24); - buf[pos + 1] = (byte)(value >>> 16); - buf[pos + 2] = (byte)(value >>> 8); - buf[pos + 3] = (byte)value; - } - - public byte[] toByteArray() { - byte[] buf2 = new byte[count]; - System.arraycopy(buf, 0, buf2, 0, count); - return buf2; - } - - public void writeTo(OutputStream out) throws IOException { - out.write(buf, 0, count); - } - - public void enlarge(int delta) { - int newCount = count + delta; - if (newCount > buf.length) { - int newLen = buf.length << 1; - byte[] newBuf = new byte[newLen > newCount ? newLen : newCount]; - System.arraycopy(buf, 0, newBuf, 0, count); - buf = newBuf; - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/Bytecode.java b/src/com/wenshuo/agent/javassist/bytecode/Bytecode.java deleted file mode 100644 index e04a85f..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/Bytecode.java +++ /dev/null @@ -1,1459 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtPrimitiveType; - -class ByteVector implements Cloneable { - private byte[] buffer; - private int size; - - public ByteVector() { - buffer = new byte[64]; - size = 0; - } - - public Object clone() throws CloneNotSupportedException { - ByteVector bv = (ByteVector)super.clone(); - bv.buffer = (byte[])buffer.clone(); - return bv; - } - - public final int getSize() { return size; } - - public final byte[] copy() { - byte[] b = new byte[size]; - System.arraycopy(buffer, 0, b, 0, size); - return b; - } - - public int read(int offset) { - if (offset < 0 || size <= offset) - throw new ArrayIndexOutOfBoundsException(offset); - - return buffer[offset]; - } - - public void write(int offset, int value) { - if (offset < 0 || size <= offset) - throw new ArrayIndexOutOfBoundsException(offset); - - buffer[offset] = (byte)value; - } - - public void add(int code) { - addGap(1); - buffer[size - 1] = (byte)code; - } - - public void add(int b1, int b2) { - addGap(2); - buffer[size - 2] = (byte)b1; - buffer[size - 1] = (byte)b2; - } - - public void add(int b1, int b2, int b3, int b4) { - addGap(4); - buffer[size - 4] = (byte)b1; - buffer[size - 3] = (byte)b2; - buffer[size - 2] = (byte)b3; - buffer[size - 1] = (byte)b4; - } - - public void addGap(int length) { - if (size + length > buffer.length) { - int newSize = size << 1; - if (newSize < size + length) - newSize = size + length; - - byte[] newBuf = new byte[newSize]; - System.arraycopy(buffer, 0, newBuf, 0, size); - buffer = newBuf; - } - - size += length; - } -} - -/** - * A utility class for producing a bytecode sequence. - * - *

A Bytecode object is an unbounded array - * containing bytecode. For example, - * - *

- * ConstPool cp = ...;    // constant pool table
- * Bytecode b = new Bytecode(cp, 1, 0);
- * b.addIconst(3);
- * b.addReturn(CtClass.intType);
- * CodeAttribute ca = b.toCodeAttribute();
- * - *

This program produces a Code attribute including a bytecode - * sequence: - * - *

- * iconst_3
- * ireturn
- * - * @see ConstPool - * @see CodeAttribute - */ -public class Bytecode extends ByteVector implements Cloneable, Opcode { - /** - * Represents the CtClass file using the - * constant pool table given to this Bytecode object. - */ - public static final CtClass THIS = ConstPool.THIS; - - ConstPool constPool; - int maxStack, maxLocals; - ExceptionTable tryblocks; - private int stackDepth; - - /** - * Constructs a Bytecode object with an empty bytecode - * sequence. - * - *

The parameters stacksize and localvars - * specify initial values - * of max_stack and max_locals. - * They can be changed later. - * - * @param cp constant pool table. - * @param stacksize max_stack. - * @param localvars max_locals. - */ - public Bytecode(ConstPool cp, int stacksize, int localvars) { - constPool = cp; - maxStack = stacksize; - maxLocals = localvars; - tryblocks = new ExceptionTable(cp); - stackDepth = 0; - } - - /** - * Constructs a Bytecode object with an empty bytecode - * sequence. The initial values of max_stack and - * max_locals are zero. - * - * @param cp constant pool table. - * @see Bytecode#setMaxStack(int) - * @see Bytecode#setMaxLocals(int) - */ - public Bytecode(ConstPool cp) { - this(cp, 0, 0); - } - - /** - * Creates and returns a copy of this object. - * The constant pool object is shared between this object - * and the cloned object. - */ - public Object clone() { - try { - Bytecode bc = (Bytecode)super.clone(); - bc.tryblocks = (ExceptionTable)tryblocks.clone(); - return bc; - } - catch (CloneNotSupportedException cnse) { - throw new RuntimeException(cnse); - } - } - - /** - * Gets a constant pool table. - */ - public ConstPool getConstPool() { return constPool; } - - /** - * Returns exception_table. - */ - public ExceptionTable getExceptionTable() { return tryblocks; } - - /** - * Converts to a CodeAttribute. - */ - public CodeAttribute toCodeAttribute() { - return new CodeAttribute(constPool, maxStack, maxLocals, - get(), tryblocks); - } - - /** - * Returns the length of the bytecode sequence. - */ - public int length() { - return getSize(); - } - - /** - * Returns the produced bytecode sequence. - */ - public byte[] get() { - return copy(); - } - - /** - * Gets max_stack. - */ - public int getMaxStack() { return maxStack; } - - /** - * Sets max_stack. - * - *

This value may be automatically updated when an instruction - * is appended. A Bytecode object maintains the current - * stack depth whenever an instruction is added - * by addOpcode(). For example, if DUP is appended, - * the current stack depth is increased by one. If the new stack - * depth is more than max_stack, then it is assigned - * to max_stack. However, if branch instructions are - * appended, the current stack depth may not be correctly maintained. - * - * @see #addOpcode(int) - */ - public void setMaxStack(int size) { - maxStack = size; - } - - /** - * Gets max_locals. - */ - public int getMaxLocals() { return maxLocals; } - - /** - * Sets max_locals. - */ - public void setMaxLocals(int size) { - maxLocals = size; - } - - /** - * Sets max_locals. - * - *

This computes the number of local variables - * used to pass method parameters and sets max_locals - * to that number plus locals. - * - * @param isStatic true if params must be - * interpreted as parameters to a static method. - * @param params parameter types. - * @param locals the number of local variables excluding - * ones used to pass parameters. - */ - public void setMaxLocals(boolean isStatic, CtClass[] params, - int locals) { - if (!isStatic) - ++locals; - - if (params != null) { - CtClass doubleType = CtClass.doubleType; - CtClass longType = CtClass.longType; - int n = params.length; - for (int i = 0; i < n; ++i) { - CtClass type = params[i]; - if (type == doubleType || type == longType) - locals += 2; - else - ++locals; - } - } - - maxLocals = locals; - } - - /** - * Increments max_locals. - */ - public void incMaxLocals(int diff) { - maxLocals += diff; - } - - /** - * Adds a new entry of exception_table. - */ - public void addExceptionHandler(int start, int end, - int handler, CtClass type) { - addExceptionHandler(start, end, handler, - constPool.addClassInfo(type)); - } - - /** - * Adds a new entry of exception_table. - * - * @param type the fully-qualified name of a throwable class. - */ - public void addExceptionHandler(int start, int end, - int handler, String type) { - addExceptionHandler(start, end, handler, - constPool.addClassInfo(type)); - } - - /** - * Adds a new entry of exception_table. - */ - public void addExceptionHandler(int start, int end, - int handler, int type) { - tryblocks.add(start, end, handler, type); - } - - /** - * Returns the length of bytecode sequence - * that have been added so far. - */ - public int currentPc() { - return getSize(); - } - - /** - * Reads a signed 8bit value at the offset from the beginning of the - * bytecode sequence. - * - * @throws ArrayIndexOutOfBoundsException if offset is invalid. - */ - public int read(int offset) { - return super.read(offset); - } - - /** - * Reads a signed 16bit value at the offset from the beginning of the - * bytecode sequence. - */ - public int read16bit(int offset) { - int v1 = read(offset); - int v2 = read(offset + 1); - return (v1 << 8) + (v2 & 0xff); - } - - /** - * Reads a signed 32bit value at the offset from the beginning of the - * bytecode sequence. - */ - public int read32bit(int offset) { - int v1 = read16bit(offset); - int v2 = read16bit(offset + 2); - return (v1 << 16) + (v2 & 0xffff); - } - - /** - * Writes an 8bit value at the offset from the beginning of the - * bytecode sequence. - * - * @throws ArrayIndexOutOfBoundsException if offset is invalid. - */ - public void write(int offset, int value) { - super.write(offset, value); - } - - /** - * Writes an 16bit value at the offset from the beginning of the - * bytecode sequence. - */ - public void write16bit(int offset, int value) { - write(offset, value >> 8); - write(offset + 1, value); - } - - /** - * Writes an 32bit value at the offset from the beginning of the - * bytecode sequence. - */ - public void write32bit(int offset, int value) { - write16bit(offset, value >> 16); - write16bit(offset + 2, value); - } - - /** - * Appends an 8bit value to the end of the bytecode sequence. - */ - public void add(int code) { - super.add(code); - } - - /** - * Appends a 32bit value to the end of the bytecode sequence. - */ - public void add32bit(int value) { - add(value >> 24, value >> 16, value >> 8, value); - } - - /** - * Appends the length-byte gap to the end of the bytecode sequence. - * - * @param length the gap length in byte. - */ - public void addGap(int length) { - super.addGap(length); - } - - /** - * Appends an 8bit opcode to the end of the bytecode sequence. - * The current stack depth is updated. - * max_stack is updated if the current stack depth - * is the deepest so far. - * - *

Note: some instructions such as INVOKEVIRTUAL does not - * update the current stack depth since the increment depends - * on the method signature. - * growStack() must be explicitly called. - */ - public void addOpcode(int code) { - add(code); - growStack(STACK_GROW[code]); - } - - /** - * Increases the current stack depth. - * It also updates max_stack if the current stack depth - * is the deepest so far. - * - * @param diff the number added to the current stack depth. - */ - public void growStack(int diff) { - setStackDepth(stackDepth + diff); - } - - /** - * Returns the current stack depth. - */ - public int getStackDepth() { return stackDepth; } - - /** - * Sets the current stack depth. - * It also updates max_stack if the current stack depth - * is the deepest so far. - * - * @param depth new value. - */ - public void setStackDepth(int depth) { - stackDepth = depth; - if (stackDepth > maxStack) - maxStack = stackDepth; - } - - /** - * Appends a 16bit value to the end of the bytecode sequence. - * It never changes the current stack depth. - */ - public void addIndex(int index) { - add(index >> 8, index); - } - - /** - * Appends ALOAD or (WIDE) ALOAD_<n> - * - * @param n an index into the local variable array. - */ - public void addAload(int n) { - if (n < 4) - addOpcode(42 + n); // aload_ - else if (n < 0x100) { - addOpcode(ALOAD); // aload - add(n); - } - else { - addOpcode(WIDE); - addOpcode(ALOAD); - addIndex(n); - } - } - - /** - * Appends ASTORE or (WIDE) ASTORE_<n> - * - * @param n an index into the local variable array. - */ - public void addAstore(int n) { - if (n < 4) - addOpcode(75 + n); // astore_ - else if (n < 0x100) { - addOpcode(ASTORE); // astore - add(n); - } - else { - addOpcode(WIDE); - addOpcode(ASTORE); - addIndex(n); - } - } - - /** - * Appends ICONST or ICONST_<n> - * - * @param n the pushed integer constant. - */ - public void addIconst(int n) { - if (n < 6 && -2 < n) - addOpcode(3 + n); // iconst_ -1..5 - else if (n <= 127 && -128 <= n) { - addOpcode(16); // bipush - add(n); - } - else if (n <= 32767 && -32768 <= n) { - addOpcode(17); // sipush - add(n >> 8); - add(n); - } - else - addLdc(constPool.addIntegerInfo(n)); - } - - /** - * Appends an instruction for pushing zero or null on the stack. - * If the type is void, this method does not append any instruction. - * - * @param type the type of the zero value (or null). - */ - public void addConstZero(CtClass type) { - if (type.isPrimitive()) { - if (type == CtClass.longType) - addOpcode(LCONST_0); - else if (type == CtClass.floatType) - addOpcode(FCONST_0); - else if (type == CtClass.doubleType) - addOpcode(DCONST_0); - else if (type == CtClass.voidType) - throw new RuntimeException("void type?"); - else - addOpcode(ICONST_0); - } - else - addOpcode(ACONST_NULL); - } - - /** - * Appends ILOAD or (WIDE) ILOAD_<n> - * - * @param n an index into the local variable array. - */ - public void addIload(int n) { - if (n < 4) - addOpcode(26 + n); // iload_ - else if (n < 0x100) { - addOpcode(ILOAD); // iload - add(n); - } - else { - addOpcode(WIDE); - addOpcode(ILOAD); - addIndex(n); - } - } - - /** - * Appends ISTORE or (WIDE) ISTORE_<n> - * - * @param n an index into the local variable array. - */ - public void addIstore(int n) { - if (n < 4) - addOpcode(59 + n); // istore_ - else if (n < 0x100) { - addOpcode(ISTORE); // istore - add(n); - } - else { - addOpcode(WIDE); - addOpcode(ISTORE); - addIndex(n); - } - } - - /** - * Appends LCONST or LCONST_<n> - * - * @param n the pushed long integer constant. - */ - public void addLconst(long n) { - if (n == 0 || n == 1) - addOpcode(9 + (int)n); // lconst_ - else - addLdc2w(n); - } - - /** - * Appends LLOAD or (WIDE) LLOAD_<n> - * - * @param n an index into the local variable array. - */ - public void addLload(int n) { - if (n < 4) - addOpcode(30 + n); // lload_ - else if (n < 0x100) { - addOpcode(LLOAD); // lload - add(n); - } - else { - addOpcode(WIDE); - addOpcode(LLOAD); - addIndex(n); - } - } - - /** - * Appends LSTORE or LSTORE_<n> - * - * @param n an index into the local variable array. - */ - public void addLstore(int n) { - if (n < 4) - addOpcode(63 + n); // lstore_ - else if (n < 0x100) { - addOpcode(LSTORE); // lstore - add(n); - } - else { - addOpcode(WIDE); - addOpcode(LSTORE); - addIndex(n); - } - } - - /** - * Appends DCONST or DCONST_<n> - * - * @param d the pushed double constant. - */ - public void addDconst(double d) { - if (d == 0.0 || d == 1.0) - addOpcode(14 + (int)d); // dconst_ - else - addLdc2w(d); - } - - /** - * Appends DLOAD or (WIDE) DLOAD_<n> - * - * @param n an index into the local variable array. - */ - public void addDload(int n) { - if (n < 4) - addOpcode(38 + n); // dload_ - else if (n < 0x100) { - addOpcode(DLOAD); // dload - add(n); - } - else { - addOpcode(WIDE); - addOpcode(DLOAD); - addIndex(n); - } - } - - /** - * Appends DSTORE or (WIDE) DSTORE_<n> - * - * @param n an index into the local variable array. - */ - public void addDstore(int n) { - if (n < 4) - addOpcode(71 + n); // dstore_ - else if (n < 0x100) { - addOpcode(DSTORE); // dstore - add(n); - } - else { - addOpcode(WIDE); - addOpcode(DSTORE); - addIndex(n); - } - } - - /** - * Appends FCONST or FCONST_<n> - * - * @param f the pushed float constant. - */ - public void addFconst(float f) { - if (f == 0.0f || f == 1.0f || f == 2.0f) - addOpcode(11 + (int)f); // fconst_ - else - addLdc(constPool.addFloatInfo(f)); - } - - /** - * Appends FLOAD or (WIDE) FLOAD_<n> - * - * @param n an index into the local variable array. - */ - public void addFload(int n) { - if (n < 4) - addOpcode(34 + n); // fload_ - else if (n < 0x100) { - addOpcode(FLOAD); // fload - add(n); - } - else { - addOpcode(WIDE); - addOpcode(FLOAD); - addIndex(n); - } - } - - /** - * Appends FSTORE or FSTORE_<n> - * - * @param n an index into the local variable array. - */ - public void addFstore(int n) { - if (n < 4) - addOpcode(67 + n); // fstore_ - else if (n < 0x100) { - addOpcode(FSTORE); // fstore - add(n); - } - else { - addOpcode(WIDE); - addOpcode(FSTORE); - addIndex(n); - } - } - - /** - * Appends an instruction for loading a value from the - * local variable at the index n. - * - * @param n the index. - * @param type the type of the loaded value. - * @return the size of the value (1 or 2 word). - */ - public int addLoad(int n, CtClass type) { - if (type.isPrimitive()) { - if (type == CtClass.booleanType || type == CtClass.charType - || type == CtClass.byteType || type == CtClass.shortType - || type == CtClass.intType) - addIload(n); - else if (type == CtClass.longType) { - addLload(n); - return 2; - } - else if(type == CtClass.floatType) - addFload(n); - else if(type == CtClass.doubleType) { - addDload(n); - return 2; - } - else - throw new RuntimeException("void type?"); - } - else - addAload(n); - - return 1; - } - - /** - * Appends an instruction for storing a value into the - * local variable at the index n. - * - * @param n the index. - * @param type the type of the stored value. - * @return 2 if the type is long or double. Otherwise 1. - */ - public int addStore(int n, CtClass type) { - if (type.isPrimitive()) { - if (type == CtClass.booleanType || type == CtClass.charType - || type == CtClass.byteType || type == CtClass.shortType - || type == CtClass.intType) - addIstore(n); - else if (type == CtClass.longType) { - addLstore(n); - return 2; - } - else if (type == CtClass.floatType) - addFstore(n); - else if (type == CtClass.doubleType) { - addDstore(n); - return 2; - } - else - throw new RuntimeException("void type?"); - } - else - addAstore(n); - - return 1; - } - - /** - * Appends instructions for loading all the parameters onto the - * operand stack. - * - * @param offset the index of the first parameter. It is 0 - * if the method is static. Otherwise, it is 1. - */ - public int addLoadParameters(CtClass[] params, int offset) { - int stacksize = 0; - if (params != null) { - int n = params.length; - for (int i = 0; i < n; ++i) - stacksize += addLoad(stacksize + offset, params[i]); - } - - return stacksize; - } - - /** - * Appends CHECKCAST. - * - * @param c the type. - */ - public void addCheckcast(CtClass c) { - addOpcode(CHECKCAST); - addIndex(constPool.addClassInfo(c)); - } - - /** - * Appends CHECKCAST. - * - * @param classname a fully-qualified class name. - */ - public void addCheckcast(String classname) { - addOpcode(CHECKCAST); - addIndex(constPool.addClassInfo(classname)); - } - - /** - * Appends INSTANCEOF. - * - * @param classname the class name. - */ - public void addInstanceof(String classname) { - addOpcode(INSTANCEOF); - addIndex(constPool.addClassInfo(classname)); - } - - /** - * Appends GETFIELD. - * - * @param c the class. - * @param name the field name. - * @param type the descriptor of the field type. - * - * @see Descriptor#of(CtClass) - */ - public void addGetfield(CtClass c, String name, String type) { - add(GETFIELD); - int ci = constPool.addClassInfo(c); - addIndex(constPool.addFieldrefInfo(ci, name, type)); - growStack(Descriptor.dataSize(type) - 1); - } - - /** - * Appends GETFIELD. - * - * @param c the fully-qualified class name. - * @param name the field name. - * @param type the descriptor of the field type. - * - * @see Descriptor#of(CtClass) - */ - public void addGetfield(String c, String name, String type) { - add(GETFIELD); - int ci = constPool.addClassInfo(c); - addIndex(constPool.addFieldrefInfo(ci, name, type)); - growStack(Descriptor.dataSize(type) - 1); - } - - /** - * Appends GETSTATIC. - * - * @param c the class - * @param name the field name - * @param type the descriptor of the field type. - * - * @see Descriptor#of(CtClass) - */ - public void addGetstatic(CtClass c, String name, String type) { - add(GETSTATIC); - int ci = constPool.addClassInfo(c); - addIndex(constPool.addFieldrefInfo(ci, name, type)); - growStack(Descriptor.dataSize(type)); - } - - /** - * Appends GETSTATIC. - * - * @param c the fully-qualified class name - * @param name the field name - * @param type the descriptor of the field type. - * - * @see Descriptor#of(CtClass) - */ - public void addGetstatic(String c, String name, String type) { - add(GETSTATIC); - int ci = constPool.addClassInfo(c); - addIndex(constPool.addFieldrefInfo(ci, name, type)); - growStack(Descriptor.dataSize(type)); - } - - /** - * Appends INVOKESPECIAL. - * - * @param clazz the target class. - * @param name the method name. - * @param returnType the return type. - * @param paramTypes the parameter types. - */ - public void addInvokespecial(CtClass clazz, String name, - CtClass returnType, CtClass[] paramTypes) { - String desc = Descriptor.ofMethod(returnType, paramTypes); - addInvokespecial(clazz, name, desc); - } - - /** - * Appends INVOKESPECIAL. - * - * @param clazz the target class. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - * @see Descriptor#ofConstructor(CtClass[]) - */ - public void addInvokespecial(CtClass clazz, String name, String desc) { - boolean isInterface = clazz == null ? false : clazz.isInterface(); - addInvokespecial(isInterface, - constPool.addClassInfo(clazz), name, desc); - } - - /** - * Appends INVOKESPECIAL. The invoked method must not be a default - * method declared in an interface. - * - * @param clazz the fully-qualified class name. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - * @see Descriptor#ofConstructor(CtClass[]) - */ - public void addInvokespecial(String clazz, String name, String desc) { - addInvokespecial(false, constPool.addClassInfo(clazz), name, desc); - } - - /** - * Appends INVOKESPECIAL. The invoked method must not be a default - * method declared in an interface. - * - * @param clazz the index of CONSTANT_Class_info - * structure. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - * @see Descriptor#ofConstructor(CtClass[]) - */ - public void addInvokespecial(int clazz, String name, String desc) { - addInvokespecial(false, clazz, name, desc); - } - - /** - * Appends INVOKESPECIAL. - * - * @param isInterface true if the invoked method is a default method - * declared in an interface. - * @param clazz the index of CONSTANT_Class_info - * structure. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - * @see Descriptor#ofConstructor(CtClass[]) - */ - public void addInvokespecial(boolean isInterface, int clazz, String name, String desc) { - add(INVOKESPECIAL); - int index; - if (isInterface) - index = constPool.addInterfaceMethodrefInfo(clazz, name, desc); - else - index = constPool.addMethodrefInfo(clazz, name, desc); - - addIndex(index); - growStack(Descriptor.dataSize(desc) - 1); - } - - /** - * Appends INVOKESTATIC. - * - * @param clazz the target class. - * @param name the method name - * @param returnType the return type. - * @param paramTypes the parameter types. - */ - public void addInvokestatic(CtClass clazz, String name, - CtClass returnType, CtClass[] paramTypes) { - String desc = Descriptor.ofMethod(returnType, paramTypes); - addInvokestatic(clazz, name, desc); - } - - /** - * Appends INVOKESTATIC. - * - * @param clazz the target class. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - */ - public void addInvokestatic(CtClass clazz, String name, String desc) { - addInvokestatic(constPool.addClassInfo(clazz), name, desc); - } - - /** - * Appends INVOKESTATIC. - * - * @param classname the fully-qualified class name. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - */ - public void addInvokestatic(String classname, String name, String desc) { - addInvokestatic(constPool.addClassInfo(classname), name, desc); - } - - /** - * Appends INVOKESTATIC. - * - * @param clazz the index of CONSTANT_Class_info - * structure. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - */ - public void addInvokestatic(int clazz, String name, String desc) { - add(INVOKESTATIC); - addIndex(constPool.addMethodrefInfo(clazz, name, desc)); - growStack(Descriptor.dataSize(desc)); - } - - /** - * Appends INVOKEVIRTUAL. - * - *

The specified method must not be an inherited method. - * It must be directly declared in the class specified - * in clazz. - * - * @param clazz the target class. - * @param name the method name - * @param returnType the return type. - * @param paramTypes the parameter types. - */ - public void addInvokevirtual(CtClass clazz, String name, - CtClass returnType, CtClass[] paramTypes) { - String desc = Descriptor.ofMethod(returnType, paramTypes); - addInvokevirtual(clazz, name, desc); - } - - /** - * Appends INVOKEVIRTUAL. - * - *

The specified method must not be an inherited method. - * It must be directly declared in the class specified - * in clazz. - * - * @param clazz the target class. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - */ - public void addInvokevirtual(CtClass clazz, String name, String desc) { - addInvokevirtual(constPool.addClassInfo(clazz), name, desc); - } - - /** - * Appends INVOKEVIRTUAL. - * - *

The specified method must not be an inherited method. - * It must be directly declared in the class specified - * in classname. - * - * @param classname the fully-qualified class name. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - */ - public void addInvokevirtual(String classname, String name, String desc) { - addInvokevirtual(constPool.addClassInfo(classname), name, desc); - } - - /** - * Appends INVOKEVIRTUAL. - * - *

The specified method must not be an inherited method. - * It must be directly declared in the class specified - * by clazz. - * - * @param clazz the index of CONSTANT_Class_info - * structure. - * @param name the method name - * @param desc the descriptor of the method signature. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - */ - public void addInvokevirtual(int clazz, String name, String desc) { - add(INVOKEVIRTUAL); - addIndex(constPool.addMethodrefInfo(clazz, name, desc)); - growStack(Descriptor.dataSize(desc) - 1); - } - - /** - * Appends INVOKEINTERFACE. - * - * @param clazz the target class. - * @param name the method name - * @param returnType the return type. - * @param paramTypes the parameter types. - * @param count the count operand of the instruction. - */ - public void addInvokeinterface(CtClass clazz, String name, - CtClass returnType, CtClass[] paramTypes, - int count) { - String desc = Descriptor.ofMethod(returnType, paramTypes); - addInvokeinterface(clazz, name, desc, count); - } - - /** - * Appends INVOKEINTERFACE. - * - * @param clazz the target class. - * @param name the method name - * @param desc the descriptor of the method signature. - * @param count the count operand of the instruction. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - */ - public void addInvokeinterface(CtClass clazz, String name, - String desc, int count) { - addInvokeinterface(constPool.addClassInfo(clazz), name, desc, - count); - } - - /** - * Appends INVOKEINTERFACE. - * - * @param classname the fully-qualified class name. - * @param name the method name - * @param desc the descriptor of the method signature. - * @param count the count operand of the instruction. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - */ - public void addInvokeinterface(String classname, String name, - String desc, int count) { - addInvokeinterface(constPool.addClassInfo(classname), name, desc, - count); - } - - /** - * Appends INVOKEINTERFACE. - * - * @param clazz the index of CONSTANT_Class_info - * structure. - * @param name the method name - * @param desc the descriptor of the method signature. - * @param count the count operand of the instruction. - * - * @see Descriptor#ofMethod(CtClass,CtClass[]) - */ - public void addInvokeinterface(int clazz, String name, - String desc, int count) { - add(INVOKEINTERFACE); - addIndex(constPool.addInterfaceMethodrefInfo(clazz, name, desc)); - add(count); - add(0); - growStack(Descriptor.dataSize(desc) - 1); - } - - /** - * Appends INVOKEDYNAMIC. - * - * @param bootstrap an index into the bootstrap_methods array - * of the bootstrap method table. - * @param name the method name. - * @param desc the method descriptor. - * @see Descriptor#ofMethod(CtClass,CtClass[]) - * @since 3.17 - */ - public void addInvokedynamic(int bootstrap, String name, String desc) { - int nt = constPool.addNameAndTypeInfo(name, desc); - int dyn = constPool.addInvokeDynamicInfo(bootstrap, nt); - add(INVOKEDYNAMIC); - addIndex(dyn); - add(0, 0); - growStack(Descriptor.dataSize(desc)); // assume ConstPool#REF_invokeStatic - } - - /** - * Appends LDC or LDC_W. The pushed item is a String - * object. - * - * @param s the character string pushed by LDC or LDC_W. - */ - public void addLdc(String s) { - addLdc(constPool.addStringInfo(s)); - } - - /** - * Appends LDC or LDC_W. - * - * @param i index into the constant pool. - */ - public void addLdc(int i) { - if (i > 0xFF) { - addOpcode(LDC_W); - addIndex(i); - } - else { - addOpcode(LDC); - add(i); - } - } - - /** - * Appends LDC2_W. The pushed item is a long value. - */ - public void addLdc2w(long l) { - addOpcode(LDC2_W); - addIndex(constPool.addLongInfo(l)); - } - - /** - * Appends LDC2_W. The pushed item is a double value. - */ - public void addLdc2w(double d) { - addOpcode(LDC2_W); - addIndex(constPool.addDoubleInfo(d)); - } - - /** - * Appends NEW. - * - * @param clazz the class of the created instance. - */ - public void addNew(CtClass clazz) { - addOpcode(NEW); - addIndex(constPool.addClassInfo(clazz)); - } - - /** - * Appends NEW. - * - * @param classname the fully-qualified class name. - */ - public void addNew(String classname) { - addOpcode(NEW); - addIndex(constPool.addClassInfo(classname)); - } - - /** - * Appends ANEWARRAY. - * - * @param classname the qualified class name of the element type. - */ - public void addAnewarray(String classname) { - addOpcode(ANEWARRAY); - addIndex(constPool.addClassInfo(classname)); - } - - /** - * Appends ICONST and ANEWARRAY. - * - * @param clazz the elememnt type. - * @param length the array length. - */ - public void addAnewarray(CtClass clazz, int length) { - addIconst(length); - addOpcode(ANEWARRAY); - addIndex(constPool.addClassInfo(clazz)); - } - - /** - * Appends NEWARRAY for primitive types. - * - * @param atype T_BOOLEAN, T_CHAR, ... - * @see Opcode - */ - public void addNewarray(int atype, int length) { - addIconst(length); - addOpcode(NEWARRAY); - add(atype); - } - - /** - * Appends MULTINEWARRAY. - * - * @param clazz the array type. - * @param dimensions the sizes of all dimensions. - * @return the length of dimensions. - */ - public int addMultiNewarray(CtClass clazz, int[] dimensions) { - int len = dimensions.length; - for (int i = 0; i < len; ++i) - addIconst(dimensions[i]); - - growStack(len); - return addMultiNewarray(clazz, len); - } - - /** - * Appends MULTINEWARRAY. The size of every dimension must have been - * already pushed on the stack. - * - * @param clazz the array type. - * @param dim the number of the dimensions. - * @return the value of dim. - */ - public int addMultiNewarray(CtClass clazz, int dim) { - add(MULTIANEWARRAY); - addIndex(constPool.addClassInfo(clazz)); - add(dim); - growStack(1 - dim); - return dim; - } - - /** - * Appends MULTINEWARRAY. - * - * @param desc the type descriptor of the created array. - * @param dim dimensions. - * @return the value of dim. - */ - public int addMultiNewarray(String desc, int dim) { - add(MULTIANEWARRAY); - addIndex(constPool.addClassInfo(desc)); - add(dim); - growStack(1 - dim); - return dim; - } - - /** - * Appends PUTFIELD. - * - * @param c the target class. - * @param name the field name. - * @param desc the descriptor of the field type. - */ - public void addPutfield(CtClass c, String name, String desc) { - addPutfield0(c, null, name, desc); - } - - /** - * Appends PUTFIELD. - * - * @param classname the fully-qualified name of the target class. - * @param name the field name. - * @param desc the descriptor of the field type. - */ - public void addPutfield(String classname, String name, String desc) { - // if classnaem is null, the target class is THIS. - addPutfield0(null, classname, name, desc); - } - - private void addPutfield0(CtClass target, String classname, - String name, String desc) { - add(PUTFIELD); - // target is null if it represents THIS. - int ci = classname == null ? constPool.addClassInfo(target) - : constPool.addClassInfo(classname); - addIndex(constPool.addFieldrefInfo(ci, name, desc)); - growStack(-1 - Descriptor.dataSize(desc)); - } - - /** - * Appends PUTSTATIC. - * - * @param c the target class. - * @param name the field name. - * @param desc the descriptor of the field type. - */ - public void addPutstatic(CtClass c, String name, String desc) { - addPutstatic0(c, null, name, desc); - } - - /** - * Appends PUTSTATIC. - * - * @param classname the fully-qualified name of the target class. - * @param fieldName the field name. - * @param desc the descriptor of the field type. - */ - public void addPutstatic(String classname, String fieldName, String desc) { - // if classname is null, the target class is THIS. - addPutstatic0(null, classname, fieldName, desc); - } - - private void addPutstatic0(CtClass target, String classname, - String fieldName, String desc) { - add(PUTSTATIC); - // target is null if it represents THIS. - int ci = classname == null ? constPool.addClassInfo(target) - : constPool.addClassInfo(classname); - addIndex(constPool.addFieldrefInfo(ci, fieldName, desc)); - growStack(-Descriptor.dataSize(desc)); - } - - /** - * Appends ARETURN, IRETURN, .., or RETURN. - * - * @param type the return type. - */ - public void addReturn(CtClass type) { - if (type == null) - addOpcode(RETURN); - else if (type.isPrimitive()) { - CtPrimitiveType ptype = (CtPrimitiveType)type; - addOpcode(ptype.getReturnOp()); - } - else - addOpcode(ARETURN); - } - - /** - * Appends RET. - * - * @param var local variable - */ - public void addRet(int var) { - if (var < 0x100) { - addOpcode(RET); - add(var); - } - else { - addOpcode(WIDE); - addOpcode(RET); - addIndex(var); - } - } - - /** - * Appends instructions for executing - * java.lang.System.println(message). - * - * @param message printed message. - */ - public void addPrintln(String message) { - addGetstatic("java.lang.System", "err", "Ljava/io/PrintStream;"); - addLdc(message); - addInvokevirtual("java.io.PrintStream", - "println", "(Ljava/lang/String;)V"); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ClassFile.java b/src/com/wenshuo/agent/javassist/bytecode/ClassFile.java deleted file mode 100644 index 5a6a70c..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ClassFile.java +++ /dev/null @@ -1,932 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; - -import com.wenshuo.agent.javassist.CannotCompileException; - -/** - * ClassFile represents a Java .class file, which - * consists of a constant pool, methods, fields, and attributes. - * - *

For example,

- *
- * ClassFile cf = new ClassFile(false, "test.Foo", null);
- * cf.setInterfaces(new String[] { "java.lang.Cloneable" });
- *
- * FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I");
- * f.setAccessFlags(AccessFlag.PUBLIC);
- * cf.addField(f);
- *
- * cf.write(new DataOutputStream(new FileOutputStream("Foo.class")));
- * 
- *

This code generates a class file Foo.class for the following class:

- *
- * package test;
- * class Foo implements Cloneable {
- *     public int width;
- * }
- * 
- * - * @see FieldInfo - * @see MethodInfo - * @see ClassFileWriter - * @see javassist.CtClass#getClassFile() - * @see javassist.ClassPool#makeClass(ClassFile) - */ -public final class ClassFile { - int major, minor; // version number - ConstPool constPool; - int thisClass; - int accessFlags; - int superClass; - int[] interfaces; - ArrayList fields; - ArrayList methods; - ArrayList attributes; - String thisclassname; // not JVM-internal name - String[] cachedInterfaces; - String cachedSuperclass; - - /** - * The major version number of class files - * for JDK 1.1. - */ - public static final int JAVA_1 = 45; - - /** - * The major version number of class files - * for JDK 1.2. - */ - public static final int JAVA_2 = 46; - - /** - * The major version number of class files - * for JDK 1.3. - */ - public static final int JAVA_3 = 47; - - /** - * The major version number of class files - * for JDK 1.4. - */ - public static final int JAVA_4 = 48; - - /** - * The major version number of class files - * for JDK 1.5. - */ - public static final int JAVA_5 = 49; - - /** - * The major version number of class files - * for JDK 1.6. - */ - public static final int JAVA_6 = 50; - - /** - * The major version number of class files - * for JDK 1.7. - */ - public static final int JAVA_7 = 51; - - /** - * The major version number of class files - * for JDK 1.8. - */ - public static final int JAVA_8 = 52; - - /** - * The major version number of class files created - * from scratch. The default value is 47 (JDK 1.3). - * It is 49 (JDK 1.5) - * if the JVM supports java.lang.StringBuilder. - * It is 50 (JDK 1.6) - * if the JVM supports java.util.zip.DeflaterInputStream. - * It is 51 (JDK 1.7) - * if the JVM supports java.lang.invoke.CallSite. - */ - public static int MAJOR_VERSION = JAVA_3; - - static { - try { - Class.forName("java.lang.StringBuilder"); - MAJOR_VERSION = JAVA_5; - Class.forName("java.util.zip.DeflaterInputStream"); - MAJOR_VERSION = JAVA_6; - Class.forName("java.lang.invoke.CallSite"); - MAJOR_VERSION = JAVA_7; - } - catch (Throwable t) {} - } - - /** - * Constructs a class file from a byte stream. - */ - public ClassFile(DataInputStream in) throws IOException { - read(in); - } - - /** - * Constructs a class file including no members. - * - * @param isInterface - * true if this is an interface. false if this is a class. - * @param classname - * a fully-qualified class name - * @param superclass - * a fully-qualified super class name or null. - */ - public ClassFile(boolean isInterface, String classname, String superclass) { - major = MAJOR_VERSION; - minor = 0; // JDK 1.3 or later - constPool = new ConstPool(classname); - thisClass = constPool.getThisClassInfo(); - if (isInterface) - accessFlags = AccessFlag.INTERFACE | AccessFlag.ABSTRACT; - else - accessFlags = AccessFlag.SUPER; - - initSuperclass(superclass); - interfaces = null; - fields = new ArrayList(); - methods = new ArrayList(); - thisclassname = classname; - - attributes = new ArrayList(); - attributes.add(new SourceFileAttribute(constPool, - getSourcefileName(thisclassname))); - } - - private void initSuperclass(String superclass) { - if (superclass != null) { - this.superClass = constPool.addClassInfo(superclass); - cachedSuperclass = superclass; - } - else { - this.superClass = constPool.addClassInfo("java.lang.Object"); - cachedSuperclass = "java.lang.Object"; - } - } - - private static String getSourcefileName(String qname) { - int index = qname.lastIndexOf('.'); - if (index >= 0) - qname = qname.substring(index + 1); - - return qname + ".java"; - } - - /** - * Eliminates dead constant pool items. If a method or a field is removed, - * the constant pool items used by that method/field become dead items. This - * method recreates a constant pool. - */ - public void compact() { - ConstPool cp = compact0(); - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - minfo.compact(cp); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - finfo.compact(cp); - } - - attributes = AttributeInfo.copyAll(attributes, cp); - constPool = cp; - } - - private ConstPool compact0() { - ConstPool cp = new ConstPool(thisclassname); - thisClass = cp.getThisClassInfo(); - String sc = getSuperclass(); - if (sc != null) - superClass = cp.addClassInfo(getSuperclass()); - - if (interfaces != null) { - int n = interfaces.length; - for (int i = 0; i < n; ++i) - interfaces[i] - = cp.addClassInfo(constPool.getClassInfo(interfaces[i])); - } - - return cp; - } - - /** - * Discards all attributes, associated with both the class file and the - * members such as a code attribute and exceptions attribute. The unused - * constant pool entries are also discarded (a new packed constant pool is - * constructed). - */ - public void prune() { - ConstPool cp = compact0(); - ArrayList newAttributes = new ArrayList(); - AttributeInfo invisibleAnnotations - = getAttribute(AnnotationsAttribute.invisibleTag); - if (invisibleAnnotations != null) { - invisibleAnnotations = invisibleAnnotations.copy(cp, null); - newAttributes.add(invisibleAnnotations); - } - - AttributeInfo visibleAnnotations - = getAttribute(AnnotationsAttribute.visibleTag); - if (visibleAnnotations != null) { - visibleAnnotations = visibleAnnotations.copy(cp, null); - newAttributes.add(visibleAnnotations); - } - - AttributeInfo signature - = getAttribute(SignatureAttribute.tag); - if (signature != null) { - signature = signature.copy(cp, null); - newAttributes.add(signature); - } - - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - minfo.prune(cp); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - finfo.prune(cp); - } - - attributes = newAttributes; - constPool = cp; - } - - /** - * Returns a constant pool table. - */ - public ConstPool getConstPool() { - return constPool; - } - - /** - * Returns true if this is an interface. - */ - public boolean isInterface() { - return (accessFlags & AccessFlag.INTERFACE) != 0; - } - - /** - * Returns true if this is a final class or interface. - */ - public boolean isFinal() { - return (accessFlags & AccessFlag.FINAL) != 0; - } - - /** - * Returns true if this is an abstract class or an interface. - */ - public boolean isAbstract() { - return (accessFlags & AccessFlag.ABSTRACT) != 0; - } - - /** - * Returns access flags. - * - * @see javassist.bytecode.AccessFlag - */ - public int getAccessFlags() { - return accessFlags; - } - - /** - * Changes access flags. - * - * @see javassist.bytecode.AccessFlag - */ - public void setAccessFlags(int acc) { - if ((acc & AccessFlag.INTERFACE) == 0) - acc |= AccessFlag.SUPER; - - accessFlags = acc; - } - - /** - * Returns access and property flags of this nested class. - * This method returns -1 if the class is not a nested class. - * - *

The returned value is obtained from inner_class_access_flags - * of the entry representing this nested class itself - * in InnerClasses_attribute. - */ - public int getInnerAccessFlags() { - InnerClassesAttribute ica - = (InnerClassesAttribute)getAttribute(InnerClassesAttribute.tag); - if (ica == null) - return -1; - - String name = getName(); - int n = ica.tableLength(); - for (int i = 0; i < n; ++i) - if (name.equals(ica.innerClass(i))) - return ica.accessFlags(i); - - return -1; - } - - /** - * Returns the class name. - */ - public String getName() { - return thisclassname; - } - - /** - * Sets the class name. This method substitutes the new name for all - * occurrences of the old class name in the class file. - */ - public void setName(String name) { - renameClass(thisclassname, name); - } - - /** - * Returns the super class name. - */ - public String getSuperclass() { - if (cachedSuperclass == null) - cachedSuperclass = constPool.getClassInfo(superClass); - - return cachedSuperclass; - } - - /** - * Returns the index of the constant pool entry representing the super - * class. - */ - public int getSuperclassId() { - return superClass; - } - - /** - * Sets the super class. - * - *

- * The new super class should inherit from the old super class. - * This method modifies constructors so that they call constructors declared - * in the new super class. - */ - public void setSuperclass(String superclass) throws CannotCompileException { - if (superclass == null) - superclass = "java.lang.Object"; - - try { - this.superClass = constPool.addClassInfo(superclass); - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - minfo.setSuperclass(superclass); - } - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - cachedSuperclass = superclass; - } - - /** - * Replaces all occurrences of a class name in the class file. - * - *

- * If class X is substituted for class Y in the class file, X and Y must - * have the same signature. If Y provides a method m(), X must provide it - * even if X inherits m() from the super class. If this fact is not - * guaranteed, the bytecode verifier may cause an error. - * - * @param oldname - * the replaced class name - * @param newname - * the substituted class name - */ - public final void renameClass(String oldname, String newname) { - ArrayList list; - int n; - - if (oldname.equals(newname)) - return; - - if (oldname.equals(thisclassname)) - thisclassname = newname; - - oldname = Descriptor.toJvmName(oldname); - newname = Descriptor.toJvmName(newname); - constPool.renameClass(oldname, newname); - - AttributeInfo.renameClass(attributes, oldname, newname); - list = methods; - n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - String desc = minfo.getDescriptor(); - minfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); - AttributeInfo.renameClass(minfo.getAttributes(), oldname, newname); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - String desc = finfo.getDescriptor(); - finfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); - AttributeInfo.renameClass(finfo.getAttributes(), oldname, newname); - } - } - - /** - * Replaces all occurrences of several class names in the class file. - * - * @param classnames - * specifies which class name is replaced with which new name. - * Class names must be described with the JVM-internal - * representation like java/lang/Object. - * @see #renameClass(String,String) - */ - public final void renameClass(Map classnames) { - String jvmNewThisName = (String)classnames.get(Descriptor - .toJvmName(thisclassname)); - if (jvmNewThisName != null) - thisclassname = Descriptor.toJavaName(jvmNewThisName); - - constPool.renameClass(classnames); - - AttributeInfo.renameClass(attributes, classnames); - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - String desc = minfo.getDescriptor(); - minfo.setDescriptor(Descriptor.rename(desc, classnames)); - AttributeInfo.renameClass(minfo.getAttributes(), classnames); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - String desc = finfo.getDescriptor(); - finfo.setDescriptor(Descriptor.rename(desc, classnames)); - AttributeInfo.renameClass(finfo.getAttributes(), classnames); - } - } - - /** - * Internal-use only. - * CtClass.getRefClasses() calls this method. - */ - public final void getRefClasses(Map classnames) { - constPool.renameClass(classnames); - - AttributeInfo.getRefClasses(attributes, classnames); - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - String desc = minfo.getDescriptor(); - Descriptor.rename(desc, classnames); - AttributeInfo.getRefClasses(minfo.getAttributes(), classnames); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - String desc = finfo.getDescriptor(); - Descriptor.rename(desc, classnames); - AttributeInfo.getRefClasses(finfo.getAttributes(), classnames); - } - } - - /** - * Returns the names of the interfaces implemented by the class. - * The returned array is read only. - */ - public String[] getInterfaces() { - if (cachedInterfaces != null) - return cachedInterfaces; - - String[] rtn = null; - if (interfaces == null) - rtn = new String[0]; - else { - int n = interfaces.length; - String[] list = new String[n]; - for (int i = 0; i < n; ++i) - list[i] = constPool.getClassInfo(interfaces[i]); - - rtn = list; - } - - cachedInterfaces = rtn; - return rtn; - } - - /** - * Sets the interfaces. - * - * @param nameList - * the names of the interfaces. - */ - public void setInterfaces(String[] nameList) { - cachedInterfaces = null; - if (nameList != null) { - int n = nameList.length; - interfaces = new int[n]; - for (int i = 0; i < n; ++i) - interfaces[i] = constPool.addClassInfo(nameList[i]); - } - } - - /** - * Appends an interface to the interfaces implemented by the class. - */ - public void addInterface(String name) { - cachedInterfaces = null; - int info = constPool.addClassInfo(name); - if (interfaces == null) { - interfaces = new int[1]; - interfaces[0] = info; - } - else { - int n = interfaces.length; - int[] newarray = new int[n + 1]; - System.arraycopy(interfaces, 0, newarray, 0, n); - newarray[n] = info; - interfaces = newarray; - } - } - - /** - * Returns all the fields declared in the class. - * - * @return a list of FieldInfo. - * @see FieldInfo - */ - public List getFields() { - return fields; - } - - /** - * Appends a field to the class. - * - * @throws DuplicateMemberException when the field is already included. - */ - public void addField(FieldInfo finfo) throws DuplicateMemberException { - testExistingField(finfo.getName(), finfo.getDescriptor()); - fields.add(finfo); - } - - /** - * Just appends a field to the class. - * It does not check field duplication. - * Use this method only when minimizing performance overheads - * is seriously required. - * - * @since 3.13 - */ - public final void addField2(FieldInfo finfo) { - fields.add(finfo); - } - - private void testExistingField(String name, String descriptor) - throws DuplicateMemberException { - ListIterator it = fields.listIterator(0); - while (it.hasNext()) { - FieldInfo minfo = (FieldInfo)it.next(); - if (minfo.getName().equals(name)) - throw new DuplicateMemberException("duplicate field: " + name); - } - } - - /** - * Returns all the methods declared in the class. - * - * @return a list of MethodInfo. - * @see MethodInfo - */ - public List getMethods() { - return methods; - } - - /** - * Returns the method with the specified name. If there are multiple methods - * with that name, this method returns one of them. - * - * @return null if no such method is found. - */ - public MethodInfo getMethod(String name) { - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - if (minfo.getName().equals(name)) - return minfo; - } - - return null; - } - - /** - * Returns a static initializer (class initializer), or null if it does not - * exist. - */ - public MethodInfo getStaticInitializer() { - return getMethod(MethodInfo.nameClinit); - } - - /** - * Appends a method to the class. - * If there is a bridge method with the same name and signature, - * then the bridge method is removed before a new method is added. - * - * @throws DuplicateMemberException when the method is already included. - */ - public void addMethod(MethodInfo minfo) throws DuplicateMemberException { - testExistingMethod(minfo); - methods.add(minfo); - } - - /** - * Just appends a method to the class. - * It does not check method duplication or remove a bridge method. - * Use this method only when minimizing performance overheads - * is seriously required. - * - * @since 3.13 - */ - public final void addMethod2(MethodInfo minfo) { - methods.add(minfo); - } - - private void testExistingMethod(MethodInfo newMinfo) - throws DuplicateMemberException - { - String name = newMinfo.getName(); - String descriptor = newMinfo.getDescriptor(); - ListIterator it = methods.listIterator(0); - while (it.hasNext()) - if (isDuplicated(newMinfo, name, descriptor, (MethodInfo)it.next(), it)) - throw new DuplicateMemberException("duplicate method: " + name - + " in " + this.getName()); - } - - private static boolean isDuplicated(MethodInfo newMethod, String newName, - String newDesc, MethodInfo minfo, - ListIterator it) - { - if (!minfo.getName().equals(newName)) - return false; - - String desc = minfo.getDescriptor(); - if (!Descriptor.eqParamTypes(desc, newDesc)) - return false; - - if (desc.equals(newDesc)) { - if (notBridgeMethod(minfo)) - return true; - else { - // if the bridge method with the same signature - // already exists, replace it. - it.remove(); - return false; - } - } - else - return false; - // return notBridgeMethod(minfo) && notBridgeMethod(newMethod); - } - - /* For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed. - */ - private static boolean notBridgeMethod(MethodInfo minfo) { - return (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0; - } - - /** - * Returns all the attributes. The returned List object - * is shared with this object. If you add a new attribute to the list, - * the attribute is also added to the classs file represented by this - * object. If you remove an attribute from the list, it is also removed - * from the class file. - * - * @return a list of AttributeInfo objects. - * @see AttributeInfo - */ - public List getAttributes() { - return attributes; - } - - /** - * Returns the attribute with the specified name. If there are multiple - * attributes with that name, this method returns either of them. It - * returns null if the specified attributed is not found. - * - * @param name attribute name - * @see #getAttributes() - */ - public AttributeInfo getAttribute(String name) { - ArrayList list = attributes; - int n = list.size(); - for (int i = 0; i < n; ++i) { - AttributeInfo ai = (AttributeInfo)list.get(i); - if (ai.getName().equals(name)) - return ai; - } - - return null; - } - - /** - * Appends an attribute. If there is already an attribute with the same - * name, the new one substitutes for it. - * - * @see #getAttributes() - */ - public void addAttribute(AttributeInfo info) { - AttributeInfo.remove(attributes, info.getName()); - attributes.add(info); - } - - /** - * Returns the source file containing this class. - * - * @return null if this information is not available. - */ - public String getSourceFile() { - SourceFileAttribute sf - = (SourceFileAttribute)getAttribute(SourceFileAttribute.tag); - if (sf == null) - return null; - else - return sf.getFileName(); - } - - private void read(DataInputStream in) throws IOException { - int i, n; - int magic = in.readInt(); - if (magic != 0xCAFEBABE) - throw new IOException("bad magic number: " + Integer.toHexString(magic)); - - minor = in.readUnsignedShort(); - major = in.readUnsignedShort(); - constPool = new ConstPool(in); - accessFlags = in.readUnsignedShort(); - thisClass = in.readUnsignedShort(); - constPool.setThisClassInfo(thisClass); - superClass = in.readUnsignedShort(); - n = in.readUnsignedShort(); - if (n == 0) - interfaces = null; - else { - interfaces = new int[n]; - for (i = 0; i < n; ++i) - interfaces[i] = in.readUnsignedShort(); - } - - ConstPool cp = constPool; - n = in.readUnsignedShort(); - fields = new ArrayList(); - for (i = 0; i < n; ++i) - addField2(new FieldInfo(cp, in)); - - n = in.readUnsignedShort(); - methods = new ArrayList(); - for (i = 0; i < n; ++i) - addMethod2(new MethodInfo(cp, in)); - - attributes = new ArrayList(); - n = in.readUnsignedShort(); - for (i = 0; i < n; ++i) - addAttribute(AttributeInfo.read(cp, in)); - - thisclassname = constPool.getClassInfo(thisClass); - } - - /** - * Writes a class file represented by this object into an output stream. - */ - public void write(DataOutputStream out) throws IOException { - int i, n; - - out.writeInt(0xCAFEBABE); // magic - out.writeShort(minor); // minor version - out.writeShort(major); // major version - constPool.write(out); // constant pool - out.writeShort(accessFlags); - out.writeShort(thisClass); - out.writeShort(superClass); - - if (interfaces == null) - n = 0; - else - n = interfaces.length; - - out.writeShort(n); - for (i = 0; i < n; ++i) - out.writeShort(interfaces[i]); - - ArrayList list = fields; - n = list.size(); - out.writeShort(n); - for (i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - finfo.write(out); - } - - list = methods; - n = list.size(); - out.writeShort(n); - for (i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - minfo.write(out); - } - - out.writeShort(attributes.size()); - AttributeInfo.writeAll(attributes, out); - } - - /** - * Get the Major version. - * - * @return the major version - */ - public int getMajorVersion() { - return major; - } - - /** - * Set the major version. - * - * @param major - * the major version - */ - public void setMajorVersion(int major) { - this.major = major; - } - - /** - * Get the minor version. - * - * @return the minor version - */ - public int getMinorVersion() { - return minor; - } - - /** - * Set the minor version. - * - * @param minor - * the minor version - */ - public void setMinorVersion(int minor) { - this.minor = minor; - } - - /** - * Sets the major and minor version to Java 5. - * - * If the major version is older than 49, Java 5 - * extensions such as annotations are ignored - * by the JVM. - */ - public void setVersionToJava5() { - this.major = 49; - this.minor = 0; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java b/src/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java deleted file mode 100644 index a7bd9c7..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.PrintWriter; -import com.wenshuo.agent.javassist.Modifier; -import java.util.List; - -/** - * A utility class for priting the contents of a class file. - * It prints a constant pool table, fields, and methods in a - * human readable representation. - */ -public class ClassFilePrinter { - /** - * Prints the contents of a class file to the standard output stream. - */ - public static void print(ClassFile cf) { - print(cf, new PrintWriter(System.out, true)); - } - - /** - * Prints the contents of a class file. - */ - public static void print(ClassFile cf, PrintWriter out) { - List list; - int n; - - /* 0x0020 (SYNCHRONIZED) means ACC_SUPER if the modifiers - * are of a class. - */ - int mod - = AccessFlag.toModifier(cf.getAccessFlags() - & ~AccessFlag.SYNCHRONIZED); - out.println("major: " + cf.major + ", minor: " + cf.minor - + " modifiers: " + Integer.toHexString(cf.getAccessFlags())); - out.println(Modifier.toString(mod) + " class " - + cf.getName() + " extends " + cf.getSuperclass()); - - String[] infs = cf.getInterfaces(); - if (infs != null && infs.length > 0) { - out.print(" implements "); - out.print(infs[0]); - for (int i = 1; i < infs.length; ++i) - out.print(", " + infs[i]); - - out.println(); - } - - out.println(); - list = cf.getFields(); - n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - int acc = finfo.getAccessFlags(); - out.println(Modifier.toString(AccessFlag.toModifier(acc)) - + " " + finfo.getName() + "\t" - + finfo.getDescriptor()); - printAttributes(finfo.getAttributes(), out, 'f'); - } - - out.println(); - list = cf.getMethods(); - n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - int acc = minfo.getAccessFlags(); - out.println(Modifier.toString(AccessFlag.toModifier(acc)) - + " " + minfo.getName() + "\t" - + minfo.getDescriptor()); - printAttributes(minfo.getAttributes(), out, 'm'); - out.println(); - } - - out.println(); - printAttributes(cf.getAttributes(), out, 'c'); - } - - static void printAttributes(List list, PrintWriter out, char kind) { - if (list == null) - return; - - int n = list.size(); - for (int i = 0; i < n; ++i) { - AttributeInfo ai = (AttributeInfo)list.get(i); - if (ai instanceof CodeAttribute) { - CodeAttribute ca = (CodeAttribute)ai; - out.println("attribute: " + ai.getName() + ": " - + ai.getClass().getName()); - out.println("max stack " + ca.getMaxStack() - + ", max locals " + ca.getMaxLocals() - + ", " + ca.getExceptionTable().size() - + " catch blocks"); - out.println(""); - printAttributes(ca.getAttributes(), out, kind); - out.println(""); - } - else if (ai instanceof AnnotationsAttribute) { - out.println("annnotation: " + ai.toString()); - } - else if (ai instanceof ParameterAnnotationsAttribute) { - out.println("parameter annnotations: " + ai.toString()); - } - else if (ai instanceof StackMapTable) { - out.println(""); - StackMapTable.Printer.print((StackMapTable)ai, out); - out.println(""); - } - else if (ai instanceof StackMap) { - out.println(""); - ((StackMap)ai).print(out); - out.println(""); - } - else if (ai instanceof SignatureAttribute) { - SignatureAttribute sa = (SignatureAttribute)ai; - String sig = sa.getSignature(); - out.println("signature: " + sig); - try { - String s; - if (kind == 'c') - s = SignatureAttribute.toClassSignature(sig).toString(); - else if (kind == 'm') - s = SignatureAttribute.toMethodSignature(sig).toString(); - else - s = SignatureAttribute.toFieldSignature(sig).toString(); - - out.println(" " + s); - } - catch (BadBytecode e) { - out.println(" syntax error"); - } - } - else - out.println("attribute: " + ai.getName() - + " (" + ai.get().length + " byte): " - + ai.getClass().getName()); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java b/src/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java deleted file mode 100644 index 39ab88b..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.OutputStream; -import java.io.DataOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/** - * A quick class-file writer. This is useful when a generated - * class file is simple and the code generation should be fast. - * - *

Example: - * - *

- * ClassFileWriter cfw = new ClassFileWriter(ClassFile.JAVA_4, 0);
- * ConstPoolWriter cpw = cfw.getConstPool();
- *
- * FieldWriter fw = cfw.getFieldWriter();
- * fw.add(AccessFlag.PUBLIC, "value", "I", null);
- * fw.add(AccessFlag.PUBLIC, "value2", "J", null);
- *
- * int thisClass = cpw.addClassInfo("sample/Test");
- * int superClass = cpw.addClassInfo("java/lang/Object");
- *
- * MethodWriter mw = cfw.getMethodWriter();
- *
- * mw.begin(AccessFlag.PUBLIC, MethodInfo.nameInit, "()V", null, null);
- * mw.add(Opcode.ALOAD_0);
- * mw.add(Opcode.INVOKESPECIAL);
- * int signature = cpw.addNameAndTypeInfo(MethodInfo.nameInit, "()V");
- * mw.add16(cpw.addMethodrefInfo(superClass, signature));
- * mw.add(Opcode.RETURN);
- * mw.codeEnd(1, 1);
- * mw.end(null, null);
- *
- * mw.begin(AccessFlag.PUBLIC, "one", "()I", null, null);
- * mw.add(Opcode.ICONST_1);
- * mw.add(Opcode.IRETURN);
- * mw.codeEnd(1, 1);
- * mw.end(null, null);
- *
- * byte[] classfile = cfw.end(AccessFlag.PUBLIC, thisClass, superClass,
- *                            null, null);
- * 
- * - *

The code above generates the following class: - * - *

- * package sample;
- * public class Test {
- *     public int value;
- *     public long value2;
- *     public Test() { super(); }
- *     public one() { return 1; }
- * }
- * 
- * - * @since 3.13 - */ -public class ClassFileWriter { - private ByteStream output; - private ConstPoolWriter constPool; - private FieldWriter fields; - private MethodWriter methods; - int thisClass, superClass; - - /** - * Constructs a class file writer. - * - * @param major the major version ({@link ClassFile#JAVA_4}, {@link ClassFile#JAVA_5}, ...). - * @param minor the minor version (0 for JDK 1.3 and later). - */ - public ClassFileWriter(int major, int minor) { - output = new ByteStream(512); - output.writeInt(0xCAFEBABE); // magic - output.writeShort(minor); - output.writeShort(major); - constPool = new ConstPoolWriter(output); - fields = new FieldWriter(constPool); - methods = new MethodWriter(constPool); - - } - - /** - * Returns a constant pool. - */ - public ConstPoolWriter getConstPool() { return constPool; } - - /** - * Returns a filed writer. - */ - public FieldWriter getFieldWriter() { return fields; } - - /** - * Returns a method writer. - */ - public MethodWriter getMethodWriter() { return methods; } - - /** - * Ends writing and returns the contents of the class file. - * - * @param accessFlags access flags. - * @param thisClass this class. an index indicating its CONSTANT_Class_info. - * @param superClass super class. an index indicating its CONSTANT_Class_info. - * @param interfaces implemented interfaces. - * index numbers indicating their ClassInfo. - * It may be null. - * @param aw attributes of the class file. May be null. - * - * @see AccessFlag - */ - public byte[] end(int accessFlags, int thisClass, int superClass, - int[] interfaces, AttributeWriter aw) { - constPool.end(); - output.writeShort(accessFlags); - output.writeShort(thisClass); - output.writeShort(superClass); - if (interfaces == null) - output.writeShort(0); - else { - int n = interfaces.length; - output.writeShort(n); - for (int i = 0; i < n; i++) - output.writeShort(interfaces[i]); - } - - output.enlarge(fields.dataSize() + methods.dataSize() + 6); - try { - output.writeShort(fields.size()); - fields.write(output); - - output.writeShort(methods.numOfMethods()); - methods.write(output); - } - catch (IOException e) {} - - writeAttribute(output, aw, 0); - return output.toByteArray(); - } - - /** - * Ends writing and writes the contents of the class file into the - * given output stream. - * - * @param accessFlags access flags. - * @param thisClass this class. an index indicating its CONSTANT_Class_info. - * @param superClass super class. an index indicating its CONSTANT_Class_info. - * @param interfaces implemented interfaces. - * index numbers indicating their CONSTATNT_Class_info. - * It may be null. - * @param aw attributes of the class file. May be null. - * - * @see AccessFlag - */ - public void end(DataOutputStream out, - int accessFlags, int thisClass, int superClass, - int[] interfaces, AttributeWriter aw) - throws IOException - { - constPool.end(); - output.writeTo(out); - out.writeShort(accessFlags); - out.writeShort(thisClass); - out.writeShort(superClass); - if (interfaces == null) - out.writeShort(0); - else { - int n = interfaces.length; - out.writeShort(n); - for (int i = 0; i < n; i++) - out.writeShort(interfaces[i]); - } - - out.writeShort(fields.size()); - fields.write(out); - - out.writeShort(methods.numOfMethods()); - methods.write(out); - if (aw == null) - out.writeShort(0); - else { - out.writeShort(aw.size()); - aw.write(out); - } - } - - /** - * This writes attributes. - * - *

For example, the following object writes a synthetic attribute: - * - *

-     * ConstPoolWriter cpw = ...;
-     * final int tag = cpw.addUtf8Info("Synthetic");
-     * AttributeWriter aw = new AttributeWriter() {
-     *     public int size() {
-     *         return 1;
-     *     }
-     *     public void write(DataOutputStream out) throws java.io.IOException {
-     *         out.writeShort(tag);
-     *         out.writeInt(0);
-     *     }
-     * };
-     * 
- */ - public static interface AttributeWriter { - /** - * Returns the number of attributes that this writer will - * write. - */ - public int size(); - - /** - * Writes all the contents of the attributes. The binary representation - * of the contents is an array of attribute_info. - */ - public void write(DataOutputStream out) throws IOException; - } - - static void writeAttribute(ByteStream bs, AttributeWriter aw, int attrCount) { - if (aw == null) { - bs.writeShort(attrCount); - return; - } - - bs.writeShort(aw.size() + attrCount); - DataOutputStream dos = new DataOutputStream(bs); - try { - aw.write(dos); - dos.flush(); - } - catch (IOException e) {} - } - - /** - * Field. - */ - public static final class FieldWriter { - protected ByteStream output; - protected ConstPoolWriter constPool; - private int fieldCount; - - FieldWriter(ConstPoolWriter cp) { - output = new ByteStream(128); - constPool = cp; - fieldCount = 0; - } - - /** - * Adds a new field. - * - * @param accessFlags access flags. - * @param name the field name. - * @param descriptor the field type. - * @param aw the attributes of the field. may be null. - * @see AccessFlag - */ - public void add(int accessFlags, String name, String descriptor, AttributeWriter aw) { - int nameIndex = constPool.addUtf8Info(name); - int descIndex = constPool.addUtf8Info(descriptor); - add(accessFlags, nameIndex, descIndex, aw); - } - - /** - * Adds a new field. - * - * @param accessFlags access flags. - * @param name the field name. an index indicating its CONSTANT_Utf8_info. - * @param descriptor the field type. an index indicating its CONSTANT_Utf8_info. - * @param aw the attributes of the field. may be null. - * @see AccessFlag - */ - public void add(int accessFlags, int name, int descriptor, AttributeWriter aw) { - ++fieldCount; - output.writeShort(accessFlags); - output.writeShort(name); - output.writeShort(descriptor); - writeAttribute(output, aw, 0); - } - - int size() { return fieldCount; } - - int dataSize() { return output.size(); } - - /** - * Writes the added fields. - */ - void write(OutputStream out) throws IOException { - output.writeTo(out); - } - } - - /** - * Method. - */ - public static final class MethodWriter { - protected ByteStream output; - protected ConstPoolWriter constPool; - private int methodCount; - protected int codeIndex; - protected int throwsIndex; - protected int stackIndex; - - private int startPos; - private boolean isAbstract; - private int catchPos; - private int catchCount; - - MethodWriter(ConstPoolWriter cp) { - output = new ByteStream(256); - constPool = cp; - methodCount = 0; - codeIndex = 0; - throwsIndex = 0; - stackIndex = 0; - } - - /** - * Starts Adding a new method. - * - * @param accessFlags access flags. - * @param name the method name. - * @param descriptor the method signature. - * @param exceptions throws clause. It may be null. - * The class names must be the JVM-internal - * representations like java/lang/Exception. - * @param aw attributes to the Method_info. - */ - public void begin(int accessFlags, String name, String descriptor, - String[] exceptions, AttributeWriter aw) { - int nameIndex = constPool.addUtf8Info(name); - int descIndex = constPool.addUtf8Info(descriptor); - int[] intfs; - if (exceptions == null) - intfs = null; - else - intfs = constPool.addClassInfo(exceptions); - - begin(accessFlags, nameIndex, descIndex, intfs, aw); - } - - /** - * Starts adding a new method. - * - * @param accessFlags access flags. - * @param name the method name. an index indicating its CONSTANT_Utf8_info. - * @param descriptor the field type. an index indicating its CONSTANT_Utf8_info. - * @param exceptions throws clause. indexes indicating CONSTANT_Class_infos. - * It may be null. - * @param aw attributes to the Method_info. - */ - public void begin(int accessFlags, int name, int descriptor, int[] exceptions, AttributeWriter aw) { - ++methodCount; - output.writeShort(accessFlags); - output.writeShort(name); - output.writeShort(descriptor); - isAbstract = (accessFlags & AccessFlag.ABSTRACT) != 0; - - int attrCount = isAbstract ? 0 : 1; - if (exceptions != null) - ++attrCount; - - writeAttribute(output, aw, attrCount); - - if (exceptions != null) - writeThrows(exceptions); - - if (!isAbstract) { - if (codeIndex == 0) - codeIndex = constPool.addUtf8Info(CodeAttribute.tag); - - startPos = output.getPos(); - output.writeShort(codeIndex); - output.writeBlank(12); // attribute_length, maxStack, maxLocals, code_lenth - } - - catchPos = -1; - catchCount = 0; - } - - private void writeThrows(int[] exceptions) { - if (throwsIndex == 0) - throwsIndex = constPool.addUtf8Info(ExceptionsAttribute.tag); - - output.writeShort(throwsIndex); - output.writeInt(exceptions.length * 2 + 2); - output.writeShort(exceptions.length); - for (int i = 0; i < exceptions.length; i++) - output.writeShort(exceptions[i]); - } - - /** - * Appends an 8bit value of bytecode. - * - * @see Opcode - */ - public void add(int b) { - output.write(b); - } - - /** - * Appends a 16bit value of bytecode. - */ - public void add16(int b) { - output.writeShort(b); - } - - /** - * Appends a 32bit value of bytecode. - */ - public void add32(int b) { - output.writeInt(b); - } - - /** - * Appends a invokevirtual, inovkespecial, or invokestatic bytecode. - * - * @see Opcode - */ - public void addInvoke(int opcode, String targetClass, String methodName, - String descriptor) { - int target = constPool.addClassInfo(targetClass); - int nt = constPool.addNameAndTypeInfo(methodName, descriptor); - int method = constPool.addMethodrefInfo(target, nt); - add(opcode); - add16(method); - } - - /** - * Ends appending bytecode. - */ - public void codeEnd(int maxStack, int maxLocals) { - if (!isAbstract) { - output.writeShort(startPos + 6, maxStack); - output.writeShort(startPos + 8, maxLocals); - output.writeInt(startPos + 10, output.getPos() - startPos - 14); // code_length - catchPos = output.getPos(); - catchCount = 0; - output.writeShort(0); // number of catch clauses - } - } - - /** - * Appends an exception_table entry to the - * Code_attribute. This method is available - * only after the codeEnd method is called. - * - * @param catchType an index indicating a CONSTANT_Class_info. - */ - public void addCatch(int startPc, int endPc, int handlerPc, int catchType) { - ++catchCount; - output.writeShort(startPc); - output.writeShort(endPc); - output.writeShort(handlerPc); - output.writeShort(catchType); - } - - /** - * Ends adding a new method. The add method must be - * called before the end method is called. - * - * @param smap a stack map table. may be null. - * @param aw attributes to the Code_attribute. - * may be null. - */ - public void end(StackMapTable.Writer smap, AttributeWriter aw) { - if (isAbstract) - return; - - // exception_table_length - output.writeShort(catchPos, catchCount); - - int attrCount = smap == null ? 0 : 1; - writeAttribute(output, aw, attrCount); - - if (smap != null) { - if (stackIndex == 0) - stackIndex = constPool.addUtf8Info(StackMapTable.tag); - - output.writeShort(stackIndex); - byte[] data = smap.toByteArray(); - output.writeInt(data.length); - output.write(data); - } - - // Code attribute_length - output.writeInt(startPos + 2, output.getPos() - startPos - 6); - } - - /** - * Returns the length of the bytecode that has been added so far. - * - * @return the length in bytes. - * @since 3.19 - */ - public int size() { return output.getPos() - startPos - 14; } - - int numOfMethods() { return methodCount; } - - int dataSize() { return output.size(); } - - /** - * Writes the added methods. - */ - void write(OutputStream out) throws IOException { - output.writeTo(out); - } - } - - /** - * Constant Pool. - */ - public static final class ConstPoolWriter { - ByteStream output; - protected int startPos; - protected int num; - - ConstPoolWriter(ByteStream out) { - output = out; - startPos = out.getPos(); - num = 1; - output.writeShort(1); // number of entries - } - - /** - * Makes CONSTANT_Class_info objects for each class name. - * - * @return an array of indexes indicating CONSTANT_Class_infos. - */ - public int[] addClassInfo(String[] classNames) { - int n = classNames.length; - int[] result = new int[n]; - for (int i = 0; i < n; i++) - result[i] = addClassInfo(classNames[i]); - - return result; - } - - /** - * Adds a new CONSTANT_Class_info structure. - * - *

This also adds a CONSTANT_Utf8_info structure - * for storing the class name. - * - * @param jvmname the JVM-internal representation of a class name. - * e.g. java/lang/Object. - * @return the index of the added entry. - */ - public int addClassInfo(String jvmname) { - int utf8 = addUtf8Info(jvmname); - output.write(ClassInfo.tag); - output.writeShort(utf8); - return num++; - } - - /** - * Adds a new CONSTANT_Class_info structure. - * - * @param name name_index - * @return the index of the added entry. - */ - public int addClassInfo(int name) { - output.write(ClassInfo.tag); - output.writeShort(name); - return num++; - } - - /** - * Adds a new CONSTANT_NameAndType_info structure. - * - * @param name name_index - * @param type descriptor_index - * @return the index of the added entry. - */ - public int addNameAndTypeInfo(String name, String type) { - return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type)); - } - - /** - * Adds a new CONSTANT_NameAndType_info structure. - * - * @param name name_index - * @param type descriptor_index - * @return the index of the added entry. - */ - public int addNameAndTypeInfo(int name, int type) { - output.write(NameAndTypeInfo.tag); - output.writeShort(name); - output.writeShort(type); - return num++; - } - - /** - * Adds a new CONSTANT_Fieldref_info structure. - * - * @param classInfo class_index - * @param nameAndTypeInfo name_and_type_index. - * @return the index of the added entry. - */ - public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) { - output.write(FieldrefInfo.tag); - output.writeShort(classInfo); - output.writeShort(nameAndTypeInfo); - return num++; - } - - /** - * Adds a new CONSTANT_Methodref_info structure. - * - * @param classInfo class_index - * @param nameAndTypeInfo name_and_type_index. - * @return the index of the added entry. - */ - public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) { - output.write(MethodrefInfo.tag); - output.writeShort(classInfo); - output.writeShort(nameAndTypeInfo); - return num++; - } - - /** - * Adds a new CONSTANT_InterfaceMethodref_info - * structure. - * - * @param classInfo class_index - * @param nameAndTypeInfo name_and_type_index. - * @return the index of the added entry. - */ - public int addInterfaceMethodrefInfo(int classInfo, - int nameAndTypeInfo) { - output.write(InterfaceMethodrefInfo.tag); - output.writeShort(classInfo); - output.writeShort(nameAndTypeInfo); - return num++; - } - - /** - * Adds a new CONSTANT_MethodHandle_info - * structure. - * - * @param kind reference_kind - * such as {@link ConstPool#REF_invokeStatic REF_invokeStatic}. - * @param index reference_index. - * @return the index of the added entry. - * - * @since 3.17.1 - */ - public int addMethodHandleInfo(int kind, int index) { - output.write(MethodHandleInfo.tag); - output.write(kind); - output.writeShort(index); - return num++; - } - - /** - * Adds a new CONSTANT_MethodType_info - * structure. - * - * @param desc descriptor_index. - * @return the index of the added entry. - * - * @since 3.17.1 - */ - public int addMethodTypeInfo(int desc) { - output.write(MethodTypeInfo.tag); - output.writeShort(desc); - return num++; - } - - /** - * Adds a new CONSTANT_InvokeDynamic_info - * structure. - * - * @param bootstrap bootstrap_method_attr_index. - * @param nameAndTypeInfo name_and_type_index. - * @return the index of the added entry. - * - * @since 3.17.1 - */ - public int addInvokeDynamicInfo(int bootstrap, - int nameAndTypeInfo) { - output.write(InvokeDynamicInfo.tag); - output.writeShort(bootstrap); - output.writeShort(nameAndTypeInfo); - return num++; - } - - /** - * Adds a new CONSTANT_String_info - * structure. - * - *

This also adds a new CONSTANT_Utf8_info - * structure. - * - * @return the index of the added entry. - */ - public int addStringInfo(String str) { - int utf8 = addUtf8Info(str); - output.write(StringInfo.tag); - output.writeShort(utf8); - return num++; - } - - /** - * Adds a new CONSTANT_Integer_info - * structure. - * - * @return the index of the added entry. - */ - public int addIntegerInfo(int i) { - output.write(IntegerInfo.tag); - output.writeInt(i); - return num++; - } - - /** - * Adds a new CONSTANT_Float_info - * structure. - * - * @return the index of the added entry. - */ - public int addFloatInfo(float f) { - output.write(FloatInfo.tag); - output.writeFloat(f); - return num++; - } - - /** - * Adds a new CONSTANT_Long_info - * structure. - * - * @return the index of the added entry. - */ - public int addLongInfo(long l) { - output.write(LongInfo.tag); - output.writeLong(l); - int n = num; - num += 2; - return n; - } - - /** - * Adds a new CONSTANT_Double_info - * structure. - * - * @return the index of the added entry. - */ - public int addDoubleInfo(double d) { - output.write(DoubleInfo.tag); - output.writeDouble(d); - int n = num; - num += 2; - return n; - } - - /** - * Adds a new CONSTANT_Utf8_info - * structure. - * - * @return the index of the added entry. - */ - public int addUtf8Info(String utf8) { - output.write(Utf8Info.tag); - output.writeUTF(utf8); - return num++; - } - - /** - * Writes the contents of this class pool. - */ - void end() { - output.writeShort(startPos, num); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java b/src/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java deleted file mode 100644 index 8efc6d4..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -/** - * Utility for computing max_stack. - */ -class CodeAnalyzer implements Opcode { - private ConstPool constPool; - private CodeAttribute codeAttr; - - public CodeAnalyzer(CodeAttribute ca) { - codeAttr = ca; - constPool = ca.getConstPool(); - } - - public int computeMaxStack() - throws BadBytecode - { - /* d = stack[i] - * d == 0: not visited - * d > 0: the depth is d - 1 after executing the bytecode at i. - * d < 0: not visited. the initial depth (before execution) is 1 - d. - */ - CodeIterator ci = codeAttr.iterator(); - int length = ci.getCodeLength(); - int[] stack = new int[length]; - constPool = codeAttr.getConstPool(); - initStack(stack, codeAttr); - boolean repeat; - do { - repeat = false; - for (int i = 0; i < length; ++i) - if (stack[i] < 0) { - repeat = true; - visitBytecode(ci, stack, i); - } - } while (repeat); - - int maxStack = 1; - for (int i = 0; i < length; ++i) - if (stack[i] > maxStack) - maxStack = stack[i]; - - return maxStack - 1; // the base is 1. - } - - private void initStack(int[] stack, CodeAttribute ca) { - stack[0] = -1; - ExceptionTable et = ca.getExceptionTable(); - if (et != null) { - int size = et.size(); - for (int i = 0; i < size; ++i) - stack[et.handlerPc(i)] = -2; // an exception is on stack - } - } - - private void visitBytecode(CodeIterator ci, int[] stack, int index) - throws BadBytecode - { - int codeLength = stack.length; - ci.move(index); - int stackDepth = -stack[index]; - int[] jsrDepth = new int[1]; - jsrDepth[0] = -1; - while (ci.hasNext()) { - index = ci.next(); - stack[index] = stackDepth; - int op = ci.byteAt(index); - stackDepth = visitInst(op, ci, index, stackDepth); - if (stackDepth < 1) - throw new BadBytecode("stack underflow at " + index); - - if (processBranch(op, ci, index, codeLength, stack, stackDepth, jsrDepth)) - break; - - if (isEnd(op)) // return, ireturn, athrow, ... - break; - - if (op == JSR || op == JSR_W) - --stackDepth; - } - } - - private boolean processBranch(int opcode, CodeIterator ci, int index, - int codeLength, int[] stack, int stackDepth, int[] jsrDepth) - throws BadBytecode - { - if ((IFEQ <= opcode && opcode <= IF_ACMPNE) - || opcode == IFNULL || opcode == IFNONNULL) { - int target = index + ci.s16bitAt(index + 1); - checkTarget(index, target, codeLength, stack, stackDepth); - } - else { - int target, index2; - switch (opcode) { - case GOTO : - target = index + ci.s16bitAt(index + 1); - checkTarget(index, target, codeLength, stack, stackDepth); - return true; - case GOTO_W : - target = index + ci.s32bitAt(index + 1); - checkTarget(index, target, codeLength, stack, stackDepth); - return true; - case JSR : - case JSR_W : - if (opcode == JSR) - target = index + ci.s16bitAt(index + 1); - else - target = index + ci.s32bitAt(index + 1); - - checkTarget(index, target, codeLength, stack, stackDepth); - /* - * It is unknown which RET comes back to this JSR. - * So we assume that if the stack depth at one JSR instruction - * is N, then it is also N at other JSRs and N - 1 at all RET - * instructions. Note that STACK_GROW[JSR] is 1 since it pushes - * a return address on the operand stack. - */ - if (jsrDepth[0] < 0) { - jsrDepth[0] = stackDepth; - return false; - } - else if (stackDepth == jsrDepth[0]) - return false; - else - throw new BadBytecode( - "sorry, cannot compute this data flow due to JSR: " - + stackDepth + "," + jsrDepth[0]); - case RET : - if (jsrDepth[0] < 0) { - jsrDepth[0] = stackDepth + 1; - return false; - } - else if (stackDepth + 1 == jsrDepth[0]) - return true; - else - throw new BadBytecode( - "sorry, cannot compute this data flow due to RET: " - + stackDepth + "," + jsrDepth[0]); - case LOOKUPSWITCH : - case TABLESWITCH : - index2 = (index & ~3) + 4; - target = index + ci.s32bitAt(index2); - checkTarget(index, target, codeLength, stack, stackDepth); - if (opcode == LOOKUPSWITCH) { - int npairs = ci.s32bitAt(index2 + 4); - index2 += 12; - for (int i = 0; i < npairs; ++i) { - target = index + ci.s32bitAt(index2); - checkTarget(index, target, codeLength, - stack, stackDepth); - index2 += 8; - } - } - else { - int low = ci.s32bitAt(index2 + 4); - int high = ci.s32bitAt(index2 + 8); - int n = high - low + 1; - index2 += 12; - for (int i = 0; i < n; ++i) { - target = index + ci.s32bitAt(index2); - checkTarget(index, target, codeLength, - stack, stackDepth); - index2 += 4; - } - } - - return true; // always branch. - } - } - - return false; // may not branch. - } - - private void checkTarget(int opIndex, int target, int codeLength, - int[] stack, int stackDepth) - throws BadBytecode - { - if (target < 0 || codeLength <= target) - throw new BadBytecode("bad branch offset at " + opIndex); - - int d = stack[target]; - if (d == 0) - stack[target] = -stackDepth; - else if (d != stackDepth && d != -stackDepth) - throw new BadBytecode("verification error (" + stackDepth + - "," + d + ") at " + opIndex); - } - - private static boolean isEnd(int opcode) { - return (IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW; - } - - /** - * Visits an instruction. - */ - private int visitInst(int op, CodeIterator ci, int index, int stack) - throws BadBytecode - { - String desc; - switch (op) { - case GETFIELD : - stack += getFieldSize(ci, index) - 1; - break; - case PUTFIELD : - stack -= getFieldSize(ci, index) + 1; - break; - case GETSTATIC : - stack += getFieldSize(ci, index); - break; - case PUTSTATIC : - stack -= getFieldSize(ci, index); - break; - case INVOKEVIRTUAL : - case INVOKESPECIAL : - desc = constPool.getMethodrefType(ci.u16bitAt(index + 1)); - stack += Descriptor.dataSize(desc) - 1; - break; - case INVOKESTATIC : - desc = constPool.getMethodrefType(ci.u16bitAt(index + 1)); - stack += Descriptor.dataSize(desc); - break; - case INVOKEINTERFACE : - desc = constPool.getInterfaceMethodrefType( - ci.u16bitAt(index + 1)); - stack += Descriptor.dataSize(desc) - 1; - break; - case INVOKEDYNAMIC : - desc = constPool.getInvokeDynamicType(ci.u16bitAt(index + 1)); - stack += Descriptor.dataSize(desc); // assume CosntPool#REF_invokeStatic - break; - case ATHROW : - stack = 1; // the stack becomes empty (1 means no values). - break; - case MULTIANEWARRAY : - stack += 1 - ci.byteAt(index + 3); - break; - case WIDE : - op = ci.byteAt(index + 1); - // don't break here. - default : - stack += STACK_GROW[op]; - } - - return stack; - } - - private int getFieldSize(CodeIterator ci, int index) { - String desc = constPool.getFieldrefType(ci.u16bitAt(index + 1)); - return Descriptor.dataSize(desc); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java deleted file mode 100644 index 86e7bd9..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.List; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; - -/** - * Code_attribute. - * - *

To browse the code field of - * a Code_attribute structure, - * use CodeIterator. - * - * @see CodeIterator - * @see #iterator() - */ -public class CodeAttribute extends AttributeInfo implements Opcode { - /** - * The name of this attribute "Code". - */ - public static final String tag = "Code"; - - // code[] is stored in AttributeInfo.info. - - private int maxStack; - private int maxLocals; - private ExceptionTable exceptions; - private ArrayList attributes; - - /** - * Constructs a Code_attribute. - * - * @param cp constant pool table - * @param stack max_stack - * @param locals max_locals - * @param code code[] - * @param etable exception_table[] - */ - public CodeAttribute(ConstPool cp, int stack, int locals, byte[] code, - ExceptionTable etable) - { - super(cp, tag); - maxStack = stack; - maxLocals = locals; - info = code; - exceptions = etable; - attributes = new ArrayList(); - } - - /** - * Constructs a copy of Code_attribute. - * Specified class names are replaced during the copy. - * - * @param cp constant pool table. - * @param src source Code attribute. - * @param classnames pairs of replaced and substituted - * class names. - */ - private CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames) - throws BadBytecode - { - super(cp, tag); - - maxStack = src.getMaxStack(); - maxLocals = src.getMaxLocals(); - exceptions = src.getExceptionTable().copy(cp, classnames); - attributes = new ArrayList(); - List src_attr = src.getAttributes(); - int num = src_attr.size(); - for (int i = 0; i < num; ++i) { - AttributeInfo ai = (AttributeInfo)src_attr.get(i); - attributes.add(ai.copy(cp, classnames)); - } - - info = src.copyCode(cp, classnames, exceptions, this); - } - - CodeAttribute(ConstPool cp, int name_id, DataInputStream in) - throws IOException - { - super(cp, name_id, (byte[])null); - int attr_len = in.readInt(); - - maxStack = in.readUnsignedShort(); - maxLocals = in.readUnsignedShort(); - - int code_len = in.readInt(); - info = new byte[code_len]; - in.readFully(info); - - exceptions = new ExceptionTable(cp, in); - - attributes = new ArrayList(); - int num = in.readUnsignedShort(); - for (int i = 0; i < num; ++i) - attributes.add(AttributeInfo.read(cp, in)); - } - - /** - * Makes a copy. Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. - * @exception RuntimeCopyException if a BadBytecode - * exception is thrown, it is - * converted into - * RuntimeCopyException. - * - * @return CodeAttribute object. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) - throws RuntimeCopyException - { - try { - return new CodeAttribute(newCp, this, classnames); - } - catch (BadBytecode e) { - throw new RuntimeCopyException("bad bytecode. fatal?"); - } - } - - /** - * An exception that may be thrown by copy() - * in CodeAttribute. - */ - public static class RuntimeCopyException extends RuntimeException { - /** - * Constructs an exception. - */ - public RuntimeCopyException(String s) { - super(s); - } - } - - /** - * Returns the length of this attribute_info - * structure. - * The returned value is attribute_length + 6. - */ - public int length() { - return 18 + info.length + exceptions.size() * 8 - + AttributeInfo.getLength(attributes); - } - - void write(DataOutputStream out) throws IOException { - out.writeShort(name); // attribute_name_index - out.writeInt(length() - 6); // attribute_length - out.writeShort(maxStack); // max_stack - out.writeShort(maxLocals); // max_locals - out.writeInt(info.length); // code_length - out.write(info); // code - exceptions.write(out); - out.writeShort(attributes.size()); // attributes_count - AttributeInfo.writeAll(attributes, out); // attributes - } - - /** - * This method is not available. - * - * @throws java.lang.UnsupportedOperationException always thrown. - */ - public byte[] get() { - throw new UnsupportedOperationException("CodeAttribute.get()"); - } - - /** - * This method is not available. - * - * @throws java.lang.UnsupportedOperationException always thrown. - */ - public void set(byte[] newinfo) { - throw new UnsupportedOperationException("CodeAttribute.set()"); - } - - void renameClass(String oldname, String newname) { - AttributeInfo.renameClass(attributes, oldname, newname); - } - - void renameClass(Map classnames) { - AttributeInfo.renameClass(attributes, classnames); - } - - void getRefClasses(Map classnames) { - AttributeInfo.getRefClasses(attributes, classnames); - } - - /** - * Returns the name of the class declaring the method including - * this code attribute. - */ - public String getDeclaringClass() { - ConstPool cp = getConstPool(); - return cp.getClassName(); - } - - /** - * Returns max_stack. - */ - public int getMaxStack() { - return maxStack; - } - - /** - * Sets max_stack. - */ - public void setMaxStack(int value) { - maxStack = value; - } - - /** - * Computes the maximum stack size and sets max_stack - * to the computed size. - * - * @throws BadBytecode if this method fails in computing. - * @return the newly computed value of max_stack - */ - public int computeMaxStack() throws BadBytecode { - maxStack = new CodeAnalyzer(this).computeMaxStack(); - return maxStack; - } - - /** - * Returns max_locals. - */ - public int getMaxLocals() { - return maxLocals; - } - - /** - * Sets max_locals. - */ - public void setMaxLocals(int value) { - maxLocals = value; - } - - /** - * Returns code_length. - */ - public int getCodeLength() { - return info.length; - } - - /** - * Returns code[]. - */ - public byte[] getCode() { - return info; - } - - /** - * Sets code[]. - */ - void setCode(byte[] newinfo) { super.set(newinfo); } - - /** - * Makes a new iterator for reading this code attribute. - */ - public CodeIterator iterator() { - return new CodeIterator(this); - } - - /** - * Returns exception_table[]. - */ - public ExceptionTable getExceptionTable() { return exceptions; } - - /** - * Returns attributes[]. - * It returns a list of AttributeInfo. - * A new element can be added to the returned list - * and an existing element can be removed from the list. - * - * @see AttributeInfo - */ - public List getAttributes() { return attributes; } - - /** - * Returns the attribute with the specified name. - * If it is not found, this method returns null. - * - * @param name attribute name - * @return an AttributeInfo object or null. - */ - public AttributeInfo getAttribute(String name) { - return AttributeInfo.lookup(attributes, name); - } - - /** - * Adds a stack map table. If another copy of stack map table - * is already contained, the old one is removed. - * - * @param smt the stack map table added to this code attribute. - * If it is null, a new stack map is not added. - * Only the old stack map is removed. - */ - public void setAttribute(StackMapTable smt) { - AttributeInfo.remove(attributes, StackMapTable.tag); - if (smt != null) - attributes.add(smt); - } - - /** - * Adds a stack map table for J2ME (CLDC). If another copy of stack map table - * is already contained, the old one is removed. - * - * @param sm the stack map table added to this code attribute. - * If it is null, a new stack map is not added. - * Only the old stack map is removed. - * @since 3.12 - */ - public void setAttribute(StackMap sm) { - AttributeInfo.remove(attributes, StackMap.tag); - if (sm != null) - attributes.add(sm); - } - - /** - * Copies code. - */ - private byte[] copyCode(ConstPool destCp, Map classnames, - ExceptionTable etable, CodeAttribute destCa) - throws BadBytecode - { - int len = getCodeLength(); - byte[] newCode = new byte[len]; - destCa.info = newCode; - LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(), - newCode, destCp, classnames); - return LdcEntry.doit(newCode, ldc, etable, destCa); - } - - private static LdcEntry copyCode(byte[] code, int beginPos, int endPos, - ConstPool srcCp, byte[] newcode, - ConstPool destCp, Map classnameMap) - throws BadBytecode - { - int i2, index; - LdcEntry ldcEntry = null; - - for (int i = beginPos; i < endPos; i = i2) { - i2 = CodeIterator.nextOpcode(code, i); - byte c = code[i]; - newcode[i] = c; - switch (c & 0xff) { - case LDC_W : - case LDC2_W : - case GETSTATIC : - case PUTSTATIC : - case GETFIELD : - case PUTFIELD : - case INVOKEVIRTUAL : - case INVOKESPECIAL : - case INVOKESTATIC : - case NEW : - case ANEWARRAY : - case CHECKCAST : - case INSTANCEOF : - copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, - classnameMap); - break; - case LDC : - index = code[i + 1] & 0xff; - index = srcCp.copy(index, destCp, classnameMap); - if (index < 0x100) - newcode[i + 1] = (byte)index; - else { - newcode[i] = NOP; - newcode[i + 1] = NOP; - LdcEntry ldc = new LdcEntry(); - ldc.where = i; - ldc.index = index; - ldc.next = ldcEntry; - ldcEntry = ldc; - } - break; - case INVOKEINTERFACE : - copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, - classnameMap); - newcode[i + 3] = code[i + 3]; - newcode[i + 4] = code[i + 4]; - break; - case INVOKEDYNAMIC : - copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, - classnameMap); - newcode[i + 3] = 0; - newcode[i + 4] = 0; - break; - case MULTIANEWARRAY : - copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, - classnameMap); - newcode[i + 3] = code[i + 3]; - break; - default : - while (++i < i2) - newcode[i] = code[i]; - - break; - } - } - - return ldcEntry; - } - - private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp, - byte[] newcode, ConstPool destCp, - Map classnameMap) { - int index = ((code[i] & 0xff) << 8) | (code[i + 1] & 0xff); - index = srcCp.copy(index, destCp, classnameMap); - newcode[i] = (byte)(index >> 8); - newcode[i + 1] = (byte)index; - } - - static class LdcEntry { - LdcEntry next; - int where; - int index; - - static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable, - CodeAttribute ca) - throws BadBytecode - { - if (ldc != null) - code = CodeIterator.changeLdcToLdcW(code, etable, ca, ldc); - - /* The original code was the following: - - while (ldc != null) { - int where = ldc.where; - code = CodeIterator.insertGapCore0(code, where, 1, false, etable, ca); - code[where] = (byte)Opcode.LDC_W; - ByteArray.write16bit(ldc.index, code, where + 1); - ldc = ldc.next; - } - - But this code does not support a large method > 32KB. - */ - - return code; - } - } - - /** - * Changes the index numbers of the local variables - * to append a new parameter. - * This method does not update LocalVariableAttribute, - * LocalVariableTypeAttribute, - * StackMapTable, or StackMap. - * These attributes must be explicitly updated. - * - * @param where the index of the new parameter. - * @param size the type size of the new parameter (1 or 2). - * - * @see LocalVariableAttribute#shiftIndex(int, int) - * @see LocalVariableTypeAttribute#shiftIndex(int, int) - * @see StackMapTable#insertLocal(int, int, int) - * @see StackMap#insertLocal(int, int, int) - */ - public void insertLocalVar(int where, int size) throws BadBytecode { - CodeIterator ci = iterator(); - while (ci.hasNext()) - shiftIndex(ci, where, size); - - setMaxLocals(getMaxLocals() + size); - } - - /** - * @param lessThan If the index of the local variable is - * less than this value, it does not change. - * Otherwise, the index is increased. - * @param delta the indexes of the local variables are - * increased by this value. - */ - private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode { - int index = ci.next(); - int opcode = ci.byteAt(index); - if (opcode < ILOAD) - return; - else if (opcode < IASTORE) { - if (opcode < ILOAD_0) { - // iload, lload, fload, dload, aload - shiftIndex8(ci, index, opcode, lessThan, delta); - } - else if (opcode < IALOAD) { - // iload_0, ..., aload_3 - shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD); - } - else if (opcode < ISTORE) - return; - else if (opcode < ISTORE_0) { - // istore, lstore, ... - shiftIndex8(ci, index, opcode, lessThan, delta); - } - else { - // istore_0, ..., astore_3 - shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE); - } - } - else if (opcode == IINC) { - int var = ci.byteAt(index + 1); - if (var < lessThan) - return; - - var += delta; - if (var < 0x100) - ci.writeByte(var, index + 1); - else { - int plus = (byte)ci.byteAt(index + 2); - int pos = ci.insertExGap(3); - ci.writeByte(WIDE, pos - 3); - ci.writeByte(IINC, pos - 2); - ci.write16bit(var, pos - 1); - ci.write16bit(plus, pos + 1); - } - } - else if (opcode == RET) - shiftIndex8(ci, index, opcode, lessThan, delta); - else if (opcode == WIDE) { - int var = ci.u16bitAt(index + 2); - if (var < lessThan) - return; - - var += delta; - ci.write16bit(var, index + 2); - } - } - - private static void shiftIndex8(CodeIterator ci, int index, int opcode, - int lessThan, int delta) - throws BadBytecode - { - int var = ci.byteAt(index + 1); - if (var < lessThan) - return; - - var += delta; - if (var < 0x100) - ci.writeByte(var, index + 1); - else { - int pos = ci.insertExGap(2); - ci.writeByte(WIDE, pos - 2); - ci.writeByte(opcode, pos - 1); - ci.write16bit(var, pos); - } - } - - private static void shiftIndex0(CodeIterator ci, int index, int opcode, - int lessThan, int delta, - int opcode_i_0, int opcode_i) - throws BadBytecode - { - int var = (opcode - opcode_i_0) % 4; - if (var < lessThan) - return; - - var += delta; - if (var < 4) - ci.writeByte(opcode + delta, index); - else { - opcode = (opcode - opcode_i_0) / 4 + opcode_i; - if (var < 0x100) { - int pos = ci.insertExGap(1); - ci.writeByte(opcode, pos - 1); - ci.writeByte(var, pos); - } - else { - int pos = ci.insertExGap(3); - ci.writeByte(WIDE, pos - 1); - ci.writeByte(opcode, pos); - ci.write16bit(var, pos + 1); - } - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/CodeIterator.java b/src/com/wenshuo/agent/javassist/bytecode/CodeIterator.java deleted file mode 100644 index 7dfc316..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/CodeIterator.java +++ /dev/null @@ -1,1604 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.util.ArrayList; - -/** - * An iterator for editing a code attribute. - * - *

To directly read or edit a bytecode sequence, call {@link #byteAt(int)}, {@link #s16bitAt(int)}, - * {@link #writeByte(int, int)}, {@link #write16bit(int, int)}, and other methods. - * For example, if method refers to a CtMethod object, - * the following code substitutes the NOP instruction for the first - * instruction of the method: - * - *

- * CodeAttribute ca = method.getMethodInfo().getCodeAttribute();
- * CodeIterator ci = ca.iterator();
- * ci.writeByte(Opcode.NOP, 0);
- * - *

To visit every instruction, call {@link #next()} on a CodeIterator. - * It returns the index of the first byte of the next instruction. - * - *

If there are multiple CodeIterators referring to the - * same Code_attribute, then inserting a gap by one - * CodeIterator will break the other - * CodeIterator. - * - *

This iterator does not provide remove(). - * If a piece of code in a Code_attribute is unnecessary, - * it should be overwritten with NOP. - * - * @see CodeAttribute#iterator() - */ -public class CodeIterator implements Opcode { - protected CodeAttribute codeAttr; - protected byte[] bytecode; - protected int endPos; - protected int currentPos; - protected int mark; - - protected CodeIterator(CodeAttribute ca) { - codeAttr = ca; - bytecode = ca.getCode(); - begin(); - } - - /** - * Moves to the first instruction. - */ - public void begin() { - currentPos = mark = 0; - endPos = getCodeLength(); - } - - /** - * Moves to the given index. - * - *

The index of the next instruction is set to the given index. - * The successive call to next() - * returns the index that has been given to move(). - * - *

Note that the index is into the byte array returned by - * get().getCode(). - * - * @see CodeAttribute#getCode() - */ - public void move(int index) { - currentPos = index; - } - - /** - * Sets a mark to the bytecode at the given index. - * The mark can be used to track the position of that bytecode - * when code blocks are inserted. - * If a code block is inclusively inserted at the position of the - * bytecode, the mark is set to the inserted code block. - * - * @see #getMark() - * @since 3.11 - */ - public void setMark(int index) { - mark = index; - } - - /** - * Gets the index of the position of the mark set by - * setMark. - * - * @return the index of the position. - * @see #setMark(int) - * @since 3.11 - */ - public int getMark() { return mark; } - - /** - * Returns a Code attribute read with this iterator. - */ - public CodeAttribute get() { - return codeAttr; - } - - /** - * Returns code_length of Code_attribute. - */ - public int getCodeLength() { - return bytecode.length; - } - - /** - * Returns the unsigned 8bit value at the given index. - */ - public int byteAt(int index) { return bytecode[index] & 0xff; } - - /** - * Returns the signed 8bit value at the given index. - */ - public int signedByteAt(int index) { return bytecode[index]; } - - /** - * Writes an 8bit value at the given index. - */ - public void writeByte(int value, int index) { - bytecode[index] = (byte)value; - } - - /** - * Returns the unsigned 16bit value at the given index. - */ - public int u16bitAt(int index) { - return ByteArray.readU16bit(bytecode, index); - } - - /** - * Returns the signed 16bit value at the given index. - */ - public int s16bitAt(int index) { - return ByteArray.readS16bit(bytecode, index); - } - - /** - * Writes a 16 bit integer at the index. - */ - public void write16bit(int value, int index) { - ByteArray.write16bit(value, bytecode, index); - } - - /** - * Returns the signed 32bit value at the given index. - */ - public int s32bitAt(int index) { - return ByteArray.read32bit(bytecode, index); - } - - /** - * Writes a 32bit integer at the index. - */ - public void write32bit(int value, int index) { - ByteArray.write32bit(value, bytecode, index); - } - - /** - * Writes a byte array at the index. - * - * @param code may be a zero-length array. - */ - public void write(byte[] code, int index) { - int len = code.length; - for (int j = 0; j < len; ++j) - bytecode[index++] = code[j]; - } - - /** - * Returns true if there is more instructions. - */ - public boolean hasNext() { return currentPos < endPos; } - - /** - * Returns the index of the next instruction - * (not the operand following the current opcode). - * - *

Note that the index is into the byte array returned by - * get().getCode(). - * - * @see CodeAttribute#getCode() - * @see CodeIterator#byteAt(int) - */ - public int next() throws BadBytecode { - int pos = currentPos; - currentPos = nextOpcode(bytecode, pos); - return pos; - } - - /** - * Obtains the value that the next call - * to next() will return. - * - *

This method is side-effects free. - * Successive calls to lookAhead() return the - * same value until next() is called. - */ - public int lookAhead() { - return currentPos; - } - - /** - * Moves to the instruction for - * either super() or this(). - * - *

This method skips all the instructions for computing arguments - * to super() or this(), which should be - * placed at the beginning of a constructor body. - * - *

This method returns the index of INVOKESPECIAL instruction - * executing super() or this(). - * A successive call to next() returns the - * index of the next instruction following that INVOKESPECIAL. - * - *

This method works only for a constructor. - * - * @return the index of the INVOKESPECIAL instruction, or -1 - * if a constructor invocation is not found. - */ - public int skipConstructor() throws BadBytecode { - return skipSuperConstructor0(-1); - } - - /** - * Moves to the instruction for super(). - * - *

This method skips all the instructions for computing arguments to - * super(), which should be - * placed at the beginning of a constructor body. - * - *

This method returns the index of INVOKESPECIAL instruction - * executing super(). - * A successive call to next() returns the - * index of the next instruction following that INVOKESPECIAL. - * - *

This method works only for a constructor. - * - * @return the index of the INVOKESPECIAL instruction, or -1 - * if a super constructor invocation is not found - * but this() is found. - */ - public int skipSuperConstructor() throws BadBytecode { - return skipSuperConstructor0(0); - } - - /** - * Moves to the instruction for this(). - * - *

This method skips all the instructions for computing arguments to - * this(), which should be - * placed at the beginning of a constructor body. - * - *

This method returns the index of INVOKESPECIAL instruction - * executing this(). - * A successive call to next() returns the - * index of the next instruction following that INVOKESPECIAL. - * - *

This method works only for a constructor. - * - * @return the index of the INVOKESPECIAL instruction, or -1 - * if a explicit constructor invocation is not found - * but super() is found. - */ - public int skipThisConstructor() throws BadBytecode { - return skipSuperConstructor0(1); - } - - /* skipSuper 1: this(), 0: super(), -1: both. - */ - private int skipSuperConstructor0(int skipThis) throws BadBytecode { - begin(); - ConstPool cp = codeAttr.getConstPool(); - String thisClassName = codeAttr.getDeclaringClass(); - int nested = 0; - while (hasNext()) { - int index = next(); - int c = byteAt(index); - if (c == NEW) - ++nested; - else if (c == INVOKESPECIAL) { - int mref = ByteArray.readU16bit(bytecode, index + 1); - if (cp.getMethodrefName(mref).equals(MethodInfo.nameInit)) - if (--nested < 0) { - if (skipThis < 0) - return index; - - String cname = cp.getMethodrefClassName(mref); - if (cname.equals(thisClassName) == (skipThis > 0)) - return index; - else - break; - } - } - } - - begin(); - return -1; - } - - /** - * Inserts the given bytecode sequence - * before the next instruction that would be returned by - * next() (not before the instruction returned - * by the last call to next()). - * Branch offsets and the exception table are also updated. - * - *

If the next instruction is at the beginning of a block statement, - * then the bytecode is inserted within that block. - * - *

An extra gap may be inserted at the end of the inserted - * bytecode sequence for adjusting alignment if the code attribute - * includes LOOKUPSWITCH or TABLESWITCH. - * - * @param code inserted bytecode sequence. - * @return the index indicating the first byte of the - * inserted byte sequence. - */ - public int insert(byte[] code) - throws BadBytecode - { - return insert0(currentPos, code, false); - } - - /** - * Inserts the given bytecode sequence - * before the instruction at the given index pos. - * Branch offsets and the exception table are also updated. - * - *

If the instruction at the given index is at the beginning - * of a block statement, - * then the bytecode is inserted within that block. - * - *

An extra gap may be inserted at the end of the inserted - * bytecode sequence for adjusting alignment if the code attribute - * includes LOOKUPSWITCH or TABLESWITCH. - * - *

The index at which the byte sequence is actually inserted - * might be different from pos since some other bytes might be - * inserted at other positions (e.g. to change GOTO - * to GOTO_W). - * - * @param pos the index at which a byte sequence is inserted. - * @param code inserted bytecode sequence. - */ - public void insert(int pos, byte[] code) throws BadBytecode { - insert0(pos, code, false); - } - - /** - * Inserts the given bytecode sequence - * before the instruction at the given index pos. - * Branch offsets and the exception table are also updated. - * - *

If the instruction at the given index is at the beginning - * of a block statement, - * then the bytecode is inserted within that block. - * - *

An extra gap may be inserted at the end of the inserted - * bytecode sequence for adjusting alignment if the code attribute - * includes LOOKUPSWITCH or TABLESWITCH. - * - * @param pos the index at which a byte sequence is inserted. - * @param code inserted bytecode sequence. - * @return the index indicating the first byte of the - * inserted byte sequence, which might be - * different from pos. - * @since 3.11 - */ - public int insertAt(int pos, byte[] code) throws BadBytecode { - return insert0(pos, code, false); - } - - /** - * Inserts the given bytecode sequence exclusively - * before the next instruction that would be returned by - * next() (not before the instruction returned - * by tha last call to next()). - * Branch offsets and the exception table are also updated. - * - *

If the next instruction is at the beginning of a block statement, - * then the bytecode is excluded from that block. - * - *

An extra gap may be inserted at the end of the inserted - * bytecode sequence for adjusting alignment if the code attribute - * includes LOOKUPSWITCH or TABLESWITCH. - * - * @param code inserted bytecode sequence. - * @return the index indicating the first byte of the - * inserted byte sequence. - */ - public int insertEx(byte[] code) - throws BadBytecode - { - return insert0(currentPos, code, true); - } - - /** - * Inserts the given bytecode sequence exclusively - * before the instruction at the given index pos. - * Branch offsets and the exception table are also updated. - * - *

If the instruction at the given index is at the beginning - * of a block statement, - * then the bytecode is excluded from that block. - * - *

An extra gap may be inserted at the end of the inserted - * bytecode sequence for adjusting alignment if the code attribute - * includes LOOKUPSWITCH or TABLESWITCH. - * - *

The index at which the byte sequence is actually inserted - * might be different from pos since some other bytes might be - * inserted at other positions (e.g. to change GOTO - * to GOTO_W). - * - * @param pos the index at which a byte sequence is inserted. - * @param code inserted bytecode sequence. - */ - public void insertEx(int pos, byte[] code) throws BadBytecode { - insert0(pos, code, true); - } - - /** - * Inserts the given bytecode sequence exclusively - * before the instruction at the given index pos. - * Branch offsets and the exception table are also updated. - * - *

If the instruction at the given index is at the beginning - * of a block statement, - * then the bytecode is excluded from that block. - * - *

An extra gap may be inserted at the end of the inserted - * bytecode sequence for adjusting alignment if the code attribute - * includes LOOKUPSWITCH or TABLESWITCH. - * - * @param pos the index at which a byte sequence is inserted. - * @param code inserted bytecode sequence. - * @return the index indicating the first byte of the - * inserted byte sequence, which might be - * different from pos. - * @since 3.11 - */ - public int insertExAt(int pos, byte[] code) throws BadBytecode { - return insert0(pos, code, true); - } - - /** - * @return the index indicating the first byte of the - * inserted byte sequence. - */ - private int insert0(int pos, byte[] code, boolean exclusive) - throws BadBytecode - { - int len = code.length; - if (len <= 0) - return pos; - - // currentPos will change. - pos = insertGapAt(pos, len, exclusive).position; - - int p = pos; - for (int j = 0; j < len; ++j) - bytecode[p++] = code[j]; - - return pos; - } - - /** - * Inserts a gap - * before the next instruction that would be returned by - * next() (not before the instruction returned - * by the last call to next()). - * Branch offsets and the exception table are also updated. - * The inserted gap is filled with NOP. The gap length may be - * extended to a multiple of 4. - * - *

If the next instruction is at the beginning of a block statement, - * then the gap is inserted within that block. - * - * @param length gap length - * @return the index indicating the first byte of the inserted gap. - */ - public int insertGap(int length) throws BadBytecode { - return insertGapAt(currentPos, length, false).position; - } - - /** - * Inserts a gap in front of the instruction at the given - * index pos. - * Branch offsets and the exception table are also updated. - * The inserted gap is filled with NOP. The gap length may be - * extended to a multiple of 4. - * - *

If the instruction at the given index is at the beginning - * of a block statement, - * then the gap is inserted within that block. - * - * @param pos the index at which a gap is inserted. - * @param length gap length. - * @return the length of the inserted gap. - * It might be bigger than length. - */ - public int insertGap(int pos, int length) throws BadBytecode { - return insertGapAt(pos, length, false).length; - } - - /** - * Inserts an exclusive gap - * before the next instruction that would be returned by - * next() (not before the instruction returned - * by the last call to next()). - * Branch offsets and the exception table are also updated. - * The inserted gap is filled with NOP. The gap length may be - * extended to a multiple of 4. - * - *

If the next instruction is at the beginning of a block statement, - * then the gap is excluded from that block. - * - * @param length gap length - * @return the index indicating the first byte of the inserted gap. - */ - public int insertExGap(int length) throws BadBytecode { - return insertGapAt(currentPos, length, true).position; - } - - /** - * Inserts an exclusive gap in front of the instruction at the given - * index pos. - * Branch offsets and the exception table are also updated. - * The inserted gap is filled with NOP. The gap length may be - * extended to a multiple of 4. - * - *

If the instruction at the given index is at the beginning - * of a block statement, - * then the gap is excluded from that block. - * - * @param pos the index at which a gap is inserted. - * @param length gap length. - * @return the length of the inserted gap. - * It might be bigger than length. - */ - public int insertExGap(int pos, int length) throws BadBytecode { - return insertGapAt(pos, length, true).length; - } - - /** - * An inserted gap. - * - * @since 3.11 - */ - public static class Gap { - /** - * The position of the gap. - */ - public int position; - - /** - * The length of the gap. - */ - public int length; - } - - /** - * Inserts an inclusive or exclusive gap in front of the instruction - * at the given index pos. - * Branch offsets and the exception table in the method body - * are also updated. The inserted gap is filled with NOP. - * The gap length may be extended to a multiple of 4. - * - *

Suppose that the instruction at the given index is at the - * beginning of a block statement. If the gap is inclusive, - * then it is included within that block. If the gap is exclusive, - * then it is excluded from that block. - * - *

The index at which the gap is actually inserted - * might be different from pos since some other bytes might be - * inserted at other positions (e.g. to change GOTO - * to GOTO_W). The index is available from the Gap - * object returned by this method. - * - *

Suppose that the gap is inserted at the position of - * the next instruction that would be returned by - * next() (not the last instruction returned - * by the last call to next()). The next - * instruction returned by next() after the gap is - * inserted is still the same instruction. It is not NOP - * at the first byte of the inserted gap. - * - * @param pos the index at which a gap is inserted. - * @param length gap length. - * @param exclusive true if exclusive, otherwise false. - * @return the position and the length of the inserted gap. - * @since 3.11 - */ - public Gap insertGapAt(int pos, int length, boolean exclusive) - throws BadBytecode - { - /** - * cursorPos indicates the next bytecode whichever exclusive is - * true or false. - */ - Gap gap = new Gap(); - if (length <= 0) { - gap.position = pos; - gap.length = 0; - return gap; - } - - byte[] c; - int length2; - if (bytecode.length + length > Short.MAX_VALUE) { - // currentPos might change after calling insertGapCore0w(). - c = insertGapCore0w(bytecode, pos, length, exclusive, - get().getExceptionTable(), codeAttr, gap); - pos = gap.position; - length2 = length; // == gap.length - } - else { - int cur = currentPos; - c = insertGapCore0(bytecode, pos, length, exclusive, - get().getExceptionTable(), codeAttr); - // insertGapCore0() never changes pos. - length2 = c.length - bytecode.length; - gap.position = pos; - gap.length = length2; - if (cur >= pos) - currentPos = cur + length2; - - if (mark > pos || (mark == pos && exclusive)) - mark += length2; - } - - codeAttr.setCode(c); - bytecode = c; - endPos = getCodeLength(); - updateCursors(pos, length2); - return gap; - } - - /** - * Is called when a gap is inserted. The default implementation is empty. - * A subclass can override this method so that cursors will be updated. - * - * @param pos the position where a gap is inserted. - * @param length the length of the gap. - */ - protected void updateCursors(int pos, int length) { - // empty - } - - /** - * Copies and inserts the entries in the given exception table - * at the beginning of the exception table in the code attribute - * edited by this object. - * - * @param offset the value added to the code positions included - * in the entries. - */ - public void insert(ExceptionTable et, int offset) { - codeAttr.getExceptionTable().add(0, et, offset); - } - - /** - * Appends the given bytecode sequence at the end. - * - * @param code the bytecode appended. - * @return the position of the first byte of the appended bytecode. - */ - public int append(byte[] code) { - int size = getCodeLength(); - int len = code.length; - if (len <= 0) - return size; - - appendGap(len); - byte[] dest = bytecode; - for (int i = 0; i < len; ++i) - dest[i + size] = code[i]; - - return size; - } - - /** - * Appends a gap at the end of the bytecode sequence. - * - * @param gapLength gap length - */ - public void appendGap(int gapLength) { - byte[] code = bytecode; - int codeLength = code.length; - byte[] newcode = new byte[codeLength + gapLength]; - - int i; - for (i = 0; i < codeLength; ++i) - newcode[i] = code[i]; - - for (i = codeLength; i < codeLength + gapLength; ++i) - newcode[i] = NOP; - - codeAttr.setCode(newcode); - bytecode = newcode; - endPos = getCodeLength(); - } - - /** - * Copies and appends the entries in the given exception table - * at the end of the exception table in the code attribute - * edited by this object. - * - * @param offset the value added to the code positions included - * in the entries. - */ - public void append(ExceptionTable et, int offset) { - ExceptionTable table = codeAttr.getExceptionTable(); - table.add(table.size(), et, offset); - } - - /* opcodeLegth is used for implementing nextOpcode(). - */ - private static final int opcodeLength[] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, - 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3, - 3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, - 5, 5 - }; - // 0 .. LOOKUPSWITCH, TABLESWITCH, WIDE - - /** - * Calculates the index of the next opcode. - */ - static int nextOpcode(byte[] code, int index) - throws BadBytecode - { - int opcode; - try { - opcode = code[index] & 0xff; - } - catch (IndexOutOfBoundsException e) { - throw new BadBytecode("invalid opcode address"); - } - - try { - int len = opcodeLength[opcode]; - if (len > 0) - return index + len; - else if (opcode == WIDE) - if (code[index + 1] == (byte)IINC) // WIDE IINC - return index + 6; - else - return index + 4; // WIDE ... - else { - int index2 = (index & ~3) + 8; - if (opcode == LOOKUPSWITCH) { - int npairs = ByteArray.read32bit(code, index2); - return index2 + npairs * 8 + 4; - } - else if (opcode == TABLESWITCH) { - int low = ByteArray.read32bit(code, index2); - int high = ByteArray.read32bit(code, index2 + 4); - return index2 + (high - low + 1) * 4 + 8; - } - // else - // throw new BadBytecode(opcode); - } - } - catch (IndexOutOfBoundsException e) { - } - - // opcode is UNUSED or an IndexOutOfBoundsException was thrown. - throw new BadBytecode(opcode); - } - - // methods for implementing insertGap(). - - static class AlignmentException extends Exception {} - - /** - * insertGapCore0() inserts a gap (some NOPs). - * It cannot handle a long code sequence more than 32K. All branch offsets must be - * signed 16bits. - * - * If "where" is the beginning of a block statement and exclusive is false, - * then the inserted gap is also included in the block statement. - * "where" must indicate the first byte of an opcode. - * The inserted gap is filled with NOP. gapLength may be extended to - * a multiple of 4. - * - * This method was also called from CodeAttribute.LdcEntry.doit(). - * - * @param where It must indicate the first byte of an opcode. - */ - static byte[] insertGapCore0(byte[] code, int where, int gapLength, - boolean exclusive, ExceptionTable etable, CodeAttribute ca) - throws BadBytecode - { - if (gapLength <= 0) - return code; - - try { - return insertGapCore1(code, where, gapLength, exclusive, etable, ca); - } - catch (AlignmentException e) { - try { - return insertGapCore1(code, where, (gapLength + 3) & ~3, - exclusive, etable, ca); - } - catch (AlignmentException e2) { - throw new RuntimeException("fatal error?"); - } - } - } - - private static byte[] insertGapCore1(byte[] code, int where, int gapLength, - boolean exclusive, ExceptionTable etable, - CodeAttribute ca) - throws BadBytecode, AlignmentException - { - int codeLength = code.length; - byte[] newcode = new byte[codeLength + gapLength]; - insertGap2(code, where, gapLength, codeLength, newcode, exclusive); - etable.shiftPc(where, gapLength, exclusive); - LineNumberAttribute na - = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); - if (na != null) - na.shiftPc(where, gapLength, exclusive); - - LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( - LocalVariableAttribute.tag); - if (va != null) - va.shiftPc(where, gapLength, exclusive); - - LocalVariableAttribute vta - = (LocalVariableAttribute)ca.getAttribute( - LocalVariableAttribute.typeTag); - if (vta != null) - vta.shiftPc(where, gapLength, exclusive); - - StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); - if (smt != null) - smt.shiftPc(where, gapLength, exclusive); - - StackMap sm = (StackMap)ca.getAttribute(StackMap.tag); - if (sm != null) - sm.shiftPc(where, gapLength, exclusive); - - return newcode; - } - - private static void insertGap2(byte[] code, int where, int gapLength, - int endPos, byte[] newcode, boolean exclusive) - throws BadBytecode, AlignmentException - { - int nextPos; - int i = 0; - int j = 0; - for (; i < endPos; i = nextPos) { - if (i == where) { - int j2 = j + gapLength; - while (j < j2) - newcode[j++] = NOP; - } - - nextPos = nextOpcode(code, i); - int inst = code[i] & 0xff; - // if, if_icmp, if_acmp, goto, jsr - if ((153 <= inst && inst <= 168) - || inst == IFNULL || inst == IFNONNULL) { - /* 2bytes *signed* offset */ - int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff); - offset = newOffset(i, offset, where, gapLength, exclusive); - newcode[j] = code[i]; - ByteArray.write16bit(offset, newcode, j + 1); - j += 3; - } - else if (inst == GOTO_W || inst == JSR_W) { - /* 4bytes offset */ - int offset = ByteArray.read32bit(code, i + 1); - offset = newOffset(i, offset, where, gapLength, exclusive); - newcode[j++] = code[i]; - ByteArray.write32bit(offset, newcode, j); - j += 4; - } - else if (inst == TABLESWITCH) { - if (i != j && (gapLength & 3) != 0) - throw new AlignmentException(); - - int i2 = (i & ~3) + 4; // 0-3 byte padding - // IBM JVM 1.4.2 cannot run the following code: - // int i0 = i; - // while (i0 < i2) - // newcode[j++] = code[i0++]; - // So extracting this code into an external method. - // see JIRA JASSIST-74. - j = copyGapBytes(newcode, j, code, i, i2); - - int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2), - where, gapLength, exclusive); - ByteArray.write32bit(defaultbyte, newcode, j); - int lowbyte = ByteArray.read32bit(code, i2 + 4); - ByteArray.write32bit(lowbyte, newcode, j + 4); - int highbyte = ByteArray.read32bit(code, i2 + 8); - ByteArray.write32bit(highbyte, newcode, j + 8); - j += 12; - int i0 = i2 + 12; - i2 = i0 + (highbyte - lowbyte + 1) * 4; - while (i0 < i2) { - int offset = newOffset(i, ByteArray.read32bit(code, i0), - where, gapLength, exclusive); - ByteArray.write32bit(offset, newcode, j); - j += 4; - i0 += 4; - } - } - else if (inst == LOOKUPSWITCH) { - if (i != j && (gapLength & 3) != 0) - throw new AlignmentException(); - - int i2 = (i & ~3) + 4; // 0-3 byte padding - - // IBM JVM 1.4.2 cannot run the following code: - // int i0 = i; - // while (i0 < i2) - // newcode[j++] = code[i0++]; - // So extracting this code into an external method. - // see JIRA JASSIST-74. - j = copyGapBytes(newcode, j, code, i, i2); - - int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2), - where, gapLength, exclusive); - ByteArray.write32bit(defaultbyte, newcode, j); - int npairs = ByteArray.read32bit(code, i2 + 4); - ByteArray.write32bit(npairs, newcode, j + 4); - j += 8; - int i0 = i2 + 8; - i2 = i0 + npairs * 8; - while (i0 < i2) { - ByteArray.copy32bit(code, i0, newcode, j); - int offset = newOffset(i, - ByteArray.read32bit(code, i0 + 4), - where, gapLength, exclusive); - ByteArray.write32bit(offset, newcode, j + 4); - j += 8; - i0 += 8; - } - } - else - while (i < nextPos) - newcode[j++] = code[i++]; - } - } - - - private static int copyGapBytes(byte[] newcode, int j, byte[] code, int i, int iEnd) { - switch (iEnd - i) { - case 4: - newcode[j++] = code[i++]; - case 3: - newcode[j++] = code[i++]; - case 2: - newcode[j++] = code[i++]; - case 1: - newcode[j++] = code[i++]; - default: - } - - return j; - } - - private static int newOffset(int i, int offset, int where, - int gapLength, boolean exclusive) { - int target = i + offset; - if (i < where) { - if (where < target || (exclusive && where == target)) - offset += gapLength; - } - else if (i == where) { - // This code is different from the code in Branch#shiftOffset(). - // see JASSIST-124. - if (target < where) - offset -= gapLength; - } - else - if (target < where || (!exclusive && where == target)) - offset -= gapLength; - - return offset; - } - - static class Pointers { - int cursor; - int mark0, mark; - ExceptionTable etable; - LineNumberAttribute line; - LocalVariableAttribute vars, types; - StackMapTable stack; - StackMap stack2; - - Pointers(int cur, int m, int m0, ExceptionTable et, CodeAttribute ca) { - cursor = cur; - mark = m; - mark0 = m0; - etable = et; // non null - line = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); - vars = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag); - types = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.typeTag); - stack = (StackMapTable)ca.getAttribute(StackMapTable.tag); - stack2 = (StackMap)ca.getAttribute(StackMap.tag); - } - - void shiftPc(int where, int gapLength, boolean exclusive) throws BadBytecode { - if (where < cursor || (where == cursor && exclusive)) - cursor += gapLength; - - if (where < mark || (where == mark && exclusive)) - mark += gapLength; - - if (where < mark0 || (where == mark0 && exclusive)) - mark0 += gapLength; - - etable.shiftPc(where, gapLength, exclusive); - if (line != null) - line.shiftPc(where, gapLength, exclusive); - - if (vars != null) - vars.shiftPc(where, gapLength, exclusive); - - if (types != null) - types.shiftPc(where, gapLength, exclusive); - - if (stack != null) - stack.shiftPc(where, gapLength, exclusive); - - if (stack2 != null) - stack2.shiftPc(where, gapLength, exclusive); - } - - void shiftForSwitch(int where, int gapLength) throws BadBytecode { - if (stack != null) - stack.shiftForSwitch(where, gapLength); - - if (stack2 != null) - stack2.shiftForSwitch(where, gapLength); - } - } - - /* - * This method is called from CodeAttribute.LdcEntry.doit(). - */ - static byte[] changeLdcToLdcW(byte[] code, ExceptionTable etable, - CodeAttribute ca, CodeAttribute.LdcEntry ldcs) - throws BadBytecode - { - Pointers pointers = new Pointers(0, 0, 0, etable, ca); - ArrayList jumps = makeJumpList(code, code.length, pointers); - while (ldcs != null) { - addLdcW(ldcs, jumps); - ldcs = ldcs.next; - } - - byte[] r = insertGap2w(code, 0, 0, false, jumps, pointers); - return r; - } - - private static void addLdcW(CodeAttribute.LdcEntry ldcs, ArrayList jumps) { - int where = ldcs.where; - LdcW ldcw = new LdcW(where, ldcs.index); - int s = jumps.size(); - for (int i = 0; i < s; i++) - if (where < ((Branch)jumps.get(i)).orgPos) { - jumps.add(i, ldcw); - return; - } - - jumps.add(ldcw); - } - - /* - * insertGapCore0w() can handle a long code sequence more than 32K. - * It guarantees that the length of the inserted gap (NOPs) is equal to - * gapLength. No other NOPs except some NOPs following TABLESWITCH or - * LOOKUPSWITCH will not be inserted. - * - * Note: currentPos might be moved. - * - * @param where It must indicate the first byte of an opcode. - * @param newWhere It contains the updated index of the position where a gap - * is inserted and the length of the gap. - * It must not be null. - */ - private byte[] insertGapCore0w(byte[] code, int where, int gapLength, boolean exclusive, - ExceptionTable etable, CodeAttribute ca, Gap newWhere) - throws BadBytecode - { - if (gapLength <= 0) - return code; - - Pointers pointers = new Pointers(currentPos, mark, where, etable, ca); - ArrayList jumps = makeJumpList(code, code.length, pointers); - byte[] r = insertGap2w(code, where, gapLength, exclusive, jumps, pointers); - currentPos = pointers.cursor; - mark = pointers.mark; - int where2 = pointers.mark0; - if (where2 == currentPos && !exclusive) - currentPos += gapLength; - - if (exclusive) - where2 -= gapLength; - - newWhere.position = where2; - newWhere.length = gapLength; - return r; - } - - private static byte[] insertGap2w(byte[] code, int where, int gapLength, - boolean exclusive, ArrayList jumps, Pointers ptrs) - throws BadBytecode - { - int n = jumps.size(); - if (gapLength > 0) { - ptrs.shiftPc(where, gapLength, exclusive); - for (int i = 0; i < n; i++) - ((Branch)jumps.get(i)).shift(where, gapLength, exclusive); - } - - boolean unstable = true; - do { - while (unstable) { - unstable = false; - for (int i = 0; i < n; i++) { - Branch b = (Branch)jumps.get(i); - if (b.expanded()) { - unstable = true; - int p = b.pos; - int delta = b.deltaSize(); - ptrs.shiftPc(p, delta, false); - for (int j = 0; j < n; j++) - ((Branch)jumps.get(j)).shift(p, delta, false); - } - } - } - - for (int i = 0; i < n; i++) { - Branch b = (Branch)jumps.get(i); - int diff = b.gapChanged(); - if (diff > 0) { - unstable = true; - int p = b.pos; - ptrs.shiftPc(p, diff, false); - for (int j = 0; j < n; j++) - ((Branch)jumps.get(j)).shift(p, diff, false); - } - } - } while (unstable); - - return makeExapndedCode(code, jumps, where, gapLength); - } - - private static ArrayList makeJumpList(byte[] code, int endPos, Pointers ptrs) - throws BadBytecode - { - ArrayList jumps = new ArrayList(); - int nextPos; - for (int i = 0; i < endPos; i = nextPos) { - nextPos = nextOpcode(code, i); - int inst = code[i] & 0xff; - // if, if_icmp, if_acmp, goto, jsr - if ((153 <= inst && inst <= 168) - || inst == IFNULL || inst == IFNONNULL) { - /* 2bytes *signed* offset */ - int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff); - Branch b; - if (inst == GOTO || inst == JSR) - b = new Jump16(i, offset); - else - b = new If16(i, offset); - - jumps.add(b); - } - else if (inst == GOTO_W || inst == JSR_W) { - /* 4bytes offset */ - int offset = ByteArray.read32bit(code, i + 1); - jumps.add(new Jump32(i, offset)); - } - else if (inst == TABLESWITCH) { - int i2 = (i & ~3) + 4; // 0-3 byte padding - int defaultbyte = ByteArray.read32bit(code, i2); - int lowbyte = ByteArray.read32bit(code, i2 + 4); - int highbyte = ByteArray.read32bit(code, i2 + 8); - int i0 = i2 + 12; - int size = highbyte - lowbyte + 1; - int[] offsets = new int[size]; - for (int j = 0; j < size; j++) { - offsets[j] = ByteArray.read32bit(code, i0); - i0 += 4; - } - - jumps.add(new Table(i, defaultbyte, lowbyte, highbyte, offsets, ptrs)); - } - else if (inst == LOOKUPSWITCH) { - int i2 = (i & ~3) + 4; // 0-3 byte padding - int defaultbyte = ByteArray.read32bit(code, i2); - int npairs = ByteArray.read32bit(code, i2 + 4); - int i0 = i2 + 8; - int[] matches = new int[npairs]; - int[] offsets = new int[npairs]; - for (int j = 0; j < npairs; j++) { - matches[j] = ByteArray.read32bit(code, i0); - offsets[j] = ByteArray.read32bit(code, i0 + 4); - i0 += 8; - } - - jumps.add(new Lookup(i, defaultbyte, matches, offsets, ptrs)); - } - } - - return jumps; - } - - private static byte[] makeExapndedCode(byte[] code, ArrayList jumps, - int where, int gapLength) - throws BadBytecode - { - int n = jumps.size(); - int size = code.length + gapLength; - for (int i = 0; i < n; i++) { - Branch b = (Branch)jumps.get(i); - size += b.deltaSize(); - } - - byte[] newcode = new byte[size]; - int src = 0, dest = 0, bindex = 0; - int len = code.length; - Branch b; - int bpos; - if (0 < n) { - b = (Branch)jumps.get(0); - bpos = b.orgPos; - } - else { - b = null; - bpos = len; // src will be never equal to bpos - } - - while (src < len) { - if (src == where) { - int pos2 = dest + gapLength; - while (dest < pos2) - newcode[dest++] = NOP; - } - - if (src != bpos) - newcode[dest++] = code[src++]; - else { - int s = b.write(src, code, dest, newcode); - src += s; - dest += s + b.deltaSize(); - if (++bindex < n) { - b = (Branch)jumps.get(bindex); - bpos = b.orgPos; - } - else { - b = null; - bpos = len; - } - } - } - - return newcode; - } - - static abstract class Branch { - int pos, orgPos; - Branch(int p) { pos = orgPos = p; } - void shift(int where, int gapLength, boolean exclusive) { - if (where < pos || (where == pos && exclusive)) - pos += gapLength; - } - - static int shiftOffset(int i, int offset, int where, - int gapLength, boolean exclusive) { - int target = i + offset; - if (i < where) { - if (where < target || (exclusive && where == target)) - offset += gapLength; - } - else if (i == where) { - // This code is different from the code in CodeIterator#newOffset(). - // see JASSIST-124. - if (target < where && exclusive) - offset -= gapLength; - else if (where < target && !exclusive) - offset += gapLength; - } - else - if (target < where || (!exclusive && where == target)) - offset -= gapLength; - - return offset; - } - - boolean expanded() { return false; } - int gapChanged() { return 0; } - int deltaSize() { return 0; } // newSize - oldSize - - // This returns the original instruction size. - abstract int write(int srcPos, byte[] code, int destPos, byte[] newcode) throws BadBytecode; - } - - /* used by changeLdcToLdcW() and CodeAttribute.LdcEntry. - */ - static class LdcW extends Branch { - int index; - boolean state; - LdcW(int p, int i) { - super(p); - index = i; - state = true; - } - - boolean expanded() { - if (state) { - state = false; - return true; - } - else - return false; - } - - int deltaSize() { return 1; } - - int write(int srcPos, byte[] code, int destPos, byte[] newcode) { - newcode[destPos] = LDC_W; - ByteArray.write16bit(index, newcode, destPos + 1); - return 2; - } - } - - static abstract class Branch16 extends Branch { - int offset; - int state; - static final int BIT16 = 0; - static final int EXPAND = 1; - static final int BIT32 = 2; - - Branch16(int p, int off) { - super(p); - offset = off; - state = BIT16; - } - - void shift(int where, int gapLength, boolean exclusive) { - offset = shiftOffset(pos, offset, where, gapLength, exclusive); - super.shift(where, gapLength, exclusive); - if (state == BIT16) - if (offset < Short.MIN_VALUE || Short.MAX_VALUE < offset) - state = EXPAND; - } - - boolean expanded() { - if (state == EXPAND) { - state = BIT32; - return true; - } - else - return false; - } - - abstract int deltaSize(); - abstract void write32(int src, byte[] code, int dest, byte[] newcode); - - int write(int src, byte[] code, int dest, byte[] newcode) { - if (state == BIT32) - write32(src, code, dest, newcode); - else { - newcode[dest] = code[src]; - ByteArray.write16bit(offset, newcode, dest + 1); - } - - return 3; - } - } - - // GOTO or JSR - static class Jump16 extends Branch16 { - Jump16(int p, int off) { - super(p, off); - } - - int deltaSize() { - return state == BIT32 ? 2 : 0; - } - - void write32(int src, byte[] code, int dest, byte[] newcode) { - newcode[dest] = (byte)(((code[src] & 0xff) == GOTO) ? GOTO_W : JSR_W); - ByteArray.write32bit(offset, newcode, dest + 1); - } - } - - // if, if_icmp, or if_acmp - static class If16 extends Branch16 { - If16(int p, int off) { - super(p, off); - } - - int deltaSize() { - return state == BIT32 ? 5 : 0; - } - - void write32(int src, byte[] code, int dest, byte[] newcode) { - newcode[dest] = (byte)opcode(code[src] & 0xff); - newcode[dest + 1] = 0; - newcode[dest + 2] = 8; // branch_offset = 8 - newcode[dest + 3] = (byte)GOTO_W; - ByteArray.write32bit(offset - 3, newcode, dest + 4); - } - - int opcode(int op) { - if (op == IFNULL) - return IFNONNULL; - else if (op == IFNONNULL) - return IFNULL; - else { - if (((op - IFEQ) & 1) == 0) - return op + 1; - else - return op - 1; - } - } - } - - static class Jump32 extends Branch { - int offset; - - Jump32(int p, int off) { - super(p); - offset = off; - } - - void shift(int where, int gapLength, boolean exclusive) { - offset = shiftOffset(pos, offset, where, gapLength, exclusive); - super.shift(where, gapLength, exclusive); - } - - int write(int src, byte[] code, int dest, byte[] newcode) { - newcode[dest] = code[src]; - ByteArray.write32bit(offset, newcode, dest + 1); - return 5; - } - } - - static abstract class Switcher extends Branch { - int gap, defaultByte; - int[] offsets; - Pointers pointers; - - Switcher(int pos, int defaultByte, int[] offsets, Pointers ptrs) { - super(pos); - this.gap = 3 - (pos & 3); - this.defaultByte = defaultByte; - this.offsets = offsets; - this.pointers = ptrs; - } - - void shift(int where, int gapLength, boolean exclusive) { - int p = pos; - defaultByte = shiftOffset(p, defaultByte, where, gapLength, exclusive); - int num = offsets.length; - for (int i = 0; i < num; i++) - offsets[i] = shiftOffset(p, offsets[i], where, gapLength, exclusive); - - super.shift(where, gapLength, exclusive); - } - - int gapChanged() { - int newGap = 3 - (pos & 3); - if (newGap > gap) { - int diff = newGap - gap; - gap = newGap; - return diff; - } - - return 0; - } - - int deltaSize() { - return gap - (3 - (orgPos & 3)); - } - - int write(int src, byte[] code, int dest, byte[] newcode) throws BadBytecode { - int padding = 3 - (pos & 3); - int nops = gap - padding; - int bytecodeSize = 5 + (3 - (orgPos & 3)) + tableSize(); - if (nops > 0) - adjustOffsets(bytecodeSize, nops); - - newcode[dest++] = code[src]; - while (padding-- > 0) - newcode[dest++] = 0; - - ByteArray.write32bit(defaultByte, newcode, dest); - int size = write2(dest + 4, newcode); - dest += size + 4; - while (nops-- > 0) - newcode[dest++] = NOP; - - return 5 + (3 - (orgPos & 3)) + size; - } - - abstract int write2(int dest, byte[] newcode); - abstract int tableSize(); - - /* If the new bytecode size is shorter than the original, some NOPs - * are appended after this branch instruction (tableswitch or - * lookupswitch) to fill the gap. - * This method changes a branch offset to point to the first NOP - * if the offset originally points to the bytecode next to this - * branch instruction. Otherwise, the bytecode would contain - * dead code. It complicates the generation of StackMap and - * StackMapTable. - */ - void adjustOffsets(int size, int nops) throws BadBytecode { - pointers.shiftForSwitch(pos + size, nops); - if (defaultByte == size) - defaultByte -= nops; - - for (int i = 0; i < offsets.length; i++) - if (offsets[i] == size) - offsets[i] -= nops; - } - } - - static class Table extends Switcher { - int low, high; - - Table(int pos, int defaultByte, int low, int high, int[] offsets, Pointers ptrs) { - super(pos, defaultByte, offsets, ptrs); - this.low = low; - this.high = high; - } - - int write2(int dest, byte[] newcode) { - ByteArray.write32bit(low, newcode, dest); - ByteArray.write32bit(high, newcode, dest + 4); - int n = offsets.length; - dest += 8; - for (int i = 0; i < n; i++) { - ByteArray.write32bit(offsets[i], newcode, dest); - dest += 4; - } - - return 8 + 4 * n; - } - - int tableSize() { return 8 + 4 * offsets.length; } - } - - static class Lookup extends Switcher { - int[] matches; - - Lookup(int pos, int defaultByte, int[] matches, int[] offsets, Pointers ptrs) { - super(pos, defaultByte, offsets, ptrs); - this.matches = matches; - } - - int write2(int dest, byte[] newcode) { - int n = matches.length; - ByteArray.write32bit(n, newcode, dest); - dest += 4; - for (int i = 0; i < n; i++) { - ByteArray.write32bit(matches[i], newcode, dest); - ByteArray.write32bit(offsets[i], newcode, dest + 4); - dest += 8; - } - - return 4 + 8 * n; - } - - int tableSize() { return 4 + 8 * matches.length; } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ConstPool.java b/src/com/wenshuo/agent/javassist/bytecode/ConstPool.java deleted file mode 100644 index a7d94ae..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ConstPool.java +++ /dev/null @@ -1,1995 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.PrintWriter; -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import com.wenshuo.agent.javassist.CtClass; - -/** - * Constant pool table. - */ -public final class ConstPool { - LongVector items; - int numOfItems; - int thisClassInfo; - HashMap itemsCache; - - /** - * CONSTANT_Class - */ - public static final int CONST_Class = ClassInfo.tag; - - /** - * CONSTANT_Fieldref - */ - public static final int CONST_Fieldref = FieldrefInfo.tag; - - /** - * CONSTANT_Methodref - */ - public static final int CONST_Methodref = MethodrefInfo.tag; - - /** - * CONSTANT_InterfaceMethodref - */ - public static final int CONST_InterfaceMethodref - = InterfaceMethodrefInfo.tag; - - /** - * CONSTANT_String - */ - public static final int CONST_String = StringInfo.tag; - - /** - * CONSTANT_Integer - */ - public static final int CONST_Integer = IntegerInfo.tag; - - /** - * CONSTANT_Float - */ - public static final int CONST_Float = FloatInfo.tag; - - /** - * CONSTANT_Long - */ - public static final int CONST_Long = LongInfo.tag; - - /** - * CONSTANT_Double - */ - public static final int CONST_Double = DoubleInfo.tag; - - /** - * CONSTANT_NameAndType - */ - public static final int CONST_NameAndType = NameAndTypeInfo.tag; - - /** - * CONSTANT_Utf8 - */ - public static final int CONST_Utf8 = Utf8Info.tag; - - /** - * CONSTANT_MethodHandle - */ - public static final int CONST_MethodHandle = MethodHandleInfo.tag; - - /** - * CONSTANT_MethodHandle - */ - public static final int CONST_MethodType = MethodTypeInfo.tag; - - /** - * CONSTANT_MethodHandle - */ - public static final int CONST_InvokeDynamic = InvokeDynamicInfo.tag; - - /** - * Represents the class using this constant pool table. - */ - public static final CtClass THIS = null; - - /** - * reference_kind of CONSTANT_MethodHandle_info. - */ - public static final int REF_getField = 1; - - /** - * reference_kind of CONSTANT_MethodHandle_info. - */ - public static final int REF_getStatic = 2; - - /** - * reference_kind of CONSTANT_MethodHandle_info. - */ - public static final int REF_putField = 3; - - /** - * reference_kind of CONSTANT_MethodHandle_info. - */ - public static final int REF_putStatic = 4; - - /** - * reference_kind of CONSTANT_MethodHandle_info. - */ - public static final int REF_invokeVirtual = 5; - - /** - * reference_kind of CONSTANT_MethodHandle_info. - */ - public static final int REF_invokeStatic = 6; - - /** - * reference_kind of CONSTANT_MethodHandle_info. - */ - public static final int REF_invokeSpecial = 7; - - /** - * reference_kind of CONSTANT_MethodHandle_info. - */ - public static final int REF_newInvokeSpecial = 8; - - /** - * reference_kind of CONSTANT_MethodHandle_info. - */ - public static final int REF_invokeInterface = 9; - - /** - * Constructs a constant pool table. - * - * @param thisclass the name of the class using this constant - * pool table - */ - public ConstPool(String thisclass) { - items = new LongVector(); - itemsCache = null; - numOfItems = 0; - addItem0(null); // index 0 is reserved by the JVM. - thisClassInfo = addClassInfo(thisclass); - } - - /** - * Constructs a constant pool table from the given byte stream. - * - * @param in byte stream. - */ - public ConstPool(DataInputStream in) throws IOException { - itemsCache = null; - thisClassInfo = 0; - /* read() initializes items and numOfItems, and do addItem(null). - */ - read(in); - } - - void prune() { - itemsCache = null; - } - - /** - * Returns the number of entries in this table. - */ - public int getSize() { - return numOfItems; - } - - /** - * Returns the name of the class using this constant pool table. - */ - public String getClassName() { - return getClassInfo(thisClassInfo); - } - - /** - * Returns the index of CONSTANT_Class_info structure - * specifying the class using this constant pool table. - */ - public int getThisClassInfo() { - return thisClassInfo; - } - - void setThisClassInfo(int i) { - thisClassInfo = i; - } - - ConstInfo getItem(int n) { - return items.elementAt(n); - } - - /** - * Returns the tag field of the constant pool table - * entry at the given index. - */ - public int getTag(int index) { - return getItem(index).getTag(); - } - - /** - * Reads CONSTANT_Class_info structure - * at the given index. - * - * @return a fully-qualified class or interface name specified - * by name_index. If the type is an array - * type, this method returns an encoded name like - * [Ljava.lang.Object; (note that the separators - * are not slashes but dots). - * @see javassist.ClassPool#getCtClass(String) - */ - public String getClassInfo(int index) { - ClassInfo c = (ClassInfo)getItem(index); - if (c == null) - return null; - else - return Descriptor.toJavaName(getUtf8Info(c.name)); - } - - /** - * Reads CONSTANT_Class_info structure - * at the given index. - * - * @return the descriptor of the type specified - * by name_index. - * @see javassist.ClassPool#getCtClass(String) - * @since 3.15 - */ - public String getClassInfoByDescriptor(int index) { - ClassInfo c = (ClassInfo)getItem(index); - if (c == null) - return null; - else { - String className = getUtf8Info(c.name); - if (className.charAt(0) == '[') - return className; - else - return Descriptor.of(className); - } - } - - /** - * Reads the name_index field of the - * CONSTANT_NameAndType_info structure - * at the given index. - */ - public int getNameAndTypeName(int index) { - NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index); - return ntinfo.memberName; - } - - /** - * Reads the descriptor_index field of the - * CONSTANT_NameAndType_info structure - * at the given index. - */ - public int getNameAndTypeDescriptor(int index) { - NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index); - return ntinfo.typeDescriptor; - } - - /** - * Reads the class_index field of the - * CONSTANT_Fieldref_info, - * CONSTANT_Methodref_info, - * or CONSTANT_Interfaceref_info, - * structure at the given index. - * - * @since 3.6 - */ - public int getMemberClass(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - return minfo.classIndex; - } - - /** - * Reads the name_and_type_index field of the - * CONSTANT_Fieldref_info, - * CONSTANT_Methodref_info, - * or CONSTANT_Interfaceref_info, - * structure at the given index. - * - * @since 3.6 - */ - public int getMemberNameAndType(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - return minfo.nameAndTypeIndex; - } - - /** - * Reads the class_index field of the - * CONSTANT_Fieldref_info structure - * at the given index. - */ - public int getFieldrefClass(int index) { - FieldrefInfo finfo = (FieldrefInfo)getItem(index); - return finfo.classIndex; - } - - /** - * Reads the class_index field of the - * CONSTANT_Fieldref_info structure - * at the given index. - * - * @return the name of the class at that class_index. - */ - public String getFieldrefClassName(int index) { - FieldrefInfo f = (FieldrefInfo)getItem(index); - if (f == null) - return null; - else - return getClassInfo(f.classIndex); - } - - /** - * Reads the name_and_type_index field of the - * CONSTANT_Fieldref_info structure - * at the given index. - */ - public int getFieldrefNameAndType(int index) { - FieldrefInfo finfo = (FieldrefInfo)getItem(index); - return finfo.nameAndTypeIndex; - } - - /** - * Reads the name_index field of the - * CONSTANT_NameAndType_info structure - * indirectly specified by the given index. - * - * @param index an index to a CONSTANT_Fieldref_info. - * @return the name of the field. - */ - public String getFieldrefName(int index) { - FieldrefInfo f = (FieldrefInfo)getItem(index); - if (f == null) - return null; - else { - NameAndTypeInfo n = (NameAndTypeInfo)getItem(f.nameAndTypeIndex); - if(n == null) - return null; - else - return getUtf8Info(n.memberName); - } - } - - /** - * Reads the descriptor_index field of the - * CONSTANT_NameAndType_info structure - * indirectly specified by the given index. - * - * @param index an index to a CONSTANT_Fieldref_info. - * @return the type descriptor of the field. - */ - public String getFieldrefType(int index) { - FieldrefInfo f = (FieldrefInfo)getItem(index); - if (f == null) - return null; - else { - NameAndTypeInfo n = (NameAndTypeInfo)getItem(f.nameAndTypeIndex); - if(n == null) - return null; - else - return getUtf8Info(n.typeDescriptor); - } - } - - /** - * Reads the class_index field of the - * CONSTANT_Methodref_info structure - * at the given index. - */ - public int getMethodrefClass(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - return minfo.classIndex; - } - - /** - * Reads the class_index field of the - * CONSTANT_Methodref_info structure - * at the given index. - * - * @return the name of the class at that class_index. - */ - public String getMethodrefClassName(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - if (minfo == null) - return null; - else - return getClassInfo(minfo.classIndex); - } - - /** - * Reads the name_and_type_index field of the - * CONSTANT_Methodref_info structure - * at the given index. - */ - public int getMethodrefNameAndType(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - return minfo.nameAndTypeIndex; - } - - /** - * Reads the name_index field of the - * CONSTANT_NameAndType_info structure - * indirectly specified by the given index. - * - * @param index an index to a CONSTANT_Methodref_info. - * @return the name of the method. - */ - public String getMethodrefName(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - if (minfo == null) - return null; - else { - NameAndTypeInfo n - = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); - if(n == null) - return null; - else - return getUtf8Info(n.memberName); - } - } - - /** - * Reads the descriptor_index field of the - * CONSTANT_NameAndType_info structure - * indirectly specified by the given index. - * - * @param index an index to a CONSTANT_Methodref_info. - * @return the descriptor of the method. - */ - public String getMethodrefType(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - if (minfo == null) - return null; - else { - NameAndTypeInfo n - = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); - if(n == null) - return null; - else - return getUtf8Info(n.typeDescriptor); - } - } - - /** - * Reads the class_index field of the - * CONSTANT_InterfaceMethodref_info structure - * at the given index. - */ - public int getInterfaceMethodrefClass(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - return minfo.classIndex; - } - - /** - * Reads the class_index field of the - * CONSTANT_InterfaceMethodref_info structure - * at the given index. - * - * @return the name of the class at that class_index. - */ - public String getInterfaceMethodrefClassName(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - return getClassInfo(minfo.classIndex); - } - - /** - * Reads the name_and_type_index field of the - * CONSTANT_InterfaceMethodref_info structure - * at the given index. - */ - public int getInterfaceMethodrefNameAndType(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - return minfo.nameAndTypeIndex; - } - - /** - * Reads the name_index field of the - * CONSTANT_NameAndType_info structure - * indirectly specified by the given index. - * - * @param index an index to - * a CONSTANT_InterfaceMethodref_info. - * @return the name of the method. - */ - public String getInterfaceMethodrefName(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - if (minfo == null) - return null; - else { - NameAndTypeInfo n - = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); - if(n == null) - return null; - else - return getUtf8Info(n.memberName); - } - } - - /** - * Reads the descriptor_index field of the - * CONSTANT_NameAndType_info structure - * indirectly specified by the given index. - * - * @param index an index to - * a CONSTANT_InterfaceMethodref_info. - * @return the descriptor of the method. - */ - public String getInterfaceMethodrefType(int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - if (minfo == null) - return null; - else { - NameAndTypeInfo n - = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); - if(n == null) - return null; - else - return getUtf8Info(n.typeDescriptor); - } - } - /** - * Reads CONSTANT_Integer_info, _Float_info, - * _Long_info, _Double_info, or - * _String_info structure. - * These are used with the LDC instruction. - * - * @return a String value or a wrapped primitive-type - * value. - */ - public Object getLdcValue(int index) { - ConstInfo constInfo = this.getItem(index); - Object value = null; - if (constInfo instanceof StringInfo) - value = this.getStringInfo(index); - else if (constInfo instanceof FloatInfo) - value = new Float(getFloatInfo(index)); - else if (constInfo instanceof IntegerInfo) - value = new Integer(getIntegerInfo(index)); - else if (constInfo instanceof LongInfo) - value = new Long(getLongInfo(index)); - else if (constInfo instanceof DoubleInfo) - value = new Double(getDoubleInfo(index)); - else - value = null; - - return value; - } - - /** - * Reads CONSTANT_Integer_info structure - * at the given index. - * - * @return the value specified by this entry. - */ - public int getIntegerInfo(int index) { - IntegerInfo i = (IntegerInfo)getItem(index); - return i.value; - } - - /** - * Reads CONSTANT_Float_info structure - * at the given index. - * - * @return the value specified by this entry. - */ - public float getFloatInfo(int index) { - FloatInfo i = (FloatInfo)getItem(index); - return i.value; - } - - /** - * Reads CONSTANT_Long_info structure - * at the given index. - * - * @return the value specified by this entry. - */ - public long getLongInfo(int index) { - LongInfo i = (LongInfo)getItem(index); - return i.value; - } - - /** - * Reads CONSTANT_Double_info structure - * at the given index. - * - * @return the value specified by this entry. - */ - public double getDoubleInfo(int index) { - DoubleInfo i = (DoubleInfo)getItem(index); - return i.value; - } - - /** - * Reads CONSTANT_String_info structure - * at the given index. - * - * @return the string specified by string_index. - */ - public String getStringInfo(int index) { - StringInfo si = (StringInfo)getItem(index); - return getUtf8Info(si.string); - } - - /** - * Reads CONSTANT_utf8_info structure - * at the given index. - * - * @return the string specified by this entry. - */ - public String getUtf8Info(int index) { - Utf8Info utf = (Utf8Info)getItem(index); - return utf.string; - } - - /** - * Reads the reference_kind field of the - * CONSTANT_MethodHandle_info structure - * at the given index. - * - * @see #REF_getField - * @see #REF_getStatic - * @see #REF_invokeInterface - * @see #REF_invokeSpecial - * @see #REF_invokeStatic - * @see #REF_invokeVirtual - * @see #REF_newInvokeSpecial - * @see #REF_putField - * @see #REF_putStatic - * @since 3.17 - */ - public int getMethodHandleKind(int index) { - MethodHandleInfo mhinfo = (MethodHandleInfo)getItem(index); - return mhinfo.refKind; - } - - /** - * Reads the reference_index field of the - * CONSTANT_MethodHandle_info structure - * at the given index. - * - * @since 3.17 - */ - public int getMethodHandleIndex(int index) { - MethodHandleInfo mhinfo = (MethodHandleInfo)getItem(index); - return mhinfo.refIndex; - } - - /** - * Reads the descriptor_index field of the - * CONSTANT_MethodType_info structure - * at the given index. - * - * @since 3.17 - */ - public int getMethodTypeInfo(int index) { - MethodTypeInfo mtinfo = (MethodTypeInfo)getItem(index); - return mtinfo.descriptor; - } - - /** - * Reads the bootstrap_method_attr_index field of the - * CONSTANT_InvokeDynamic_info structure - * at the given index. - * - * @since 3.17 - */ - public int getInvokeDynamicBootstrap(int index) { - InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index); - return iv.bootstrap; - } - - /** - * Reads the name_and_type_index field of the - * CONSTANT_InvokeDynamic_info structure - * at the given index. - * - * @since 3.17 - */ - public int getInvokeDynamicNameAndType(int index) { - InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index); - return iv.nameAndType; - } - - /** - * Reads the descriptor_index field of the - * CONSTANT_NameAndType_info structure - * indirectly specified by the given index. - * - * @param index an index to a CONSTANT_InvokeDynamic_info. - * @return the descriptor of the method. - * @since 3.17 - */ - public String getInvokeDynamicType(int index) { - InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index); - if (iv == null) - return null; - else { - NameAndTypeInfo n = (NameAndTypeInfo)getItem(iv.nameAndType); - if(n == null) - return null; - else - return getUtf8Info(n.typeDescriptor); - } - } - - /** - * Determines whether CONSTANT_Methodref_info - * structure at the given index represents the constructor - * of the given class. - * - * @return the descriptor_index specifying - * the type descriptor of the that constructor. - * If it is not that constructor, - * isConstructor() returns 0. - */ - public int isConstructor(String classname, int index) { - return isMember(classname, MethodInfo.nameInit, index); - } - - /** - * Determines whether CONSTANT_Methodref_info, - * CONSTANT_Fieldref_info, or - * CONSTANT_InterfaceMethodref_info structure - * at the given index represents the member with the specified - * name and declaring class. - * - * @param classname the class declaring the member - * @param membername the member name - * @param index the index into the constant pool table - * - * @return the descriptor_index specifying - * the type descriptor of that member. - * If it is not that member, - * isMember() returns 0. - */ - public int isMember(String classname, String membername, int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - if (getClassInfo(minfo.classIndex).equals(classname)) { - NameAndTypeInfo ntinfo - = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); - if (getUtf8Info(ntinfo.memberName).equals(membername)) - return ntinfo.typeDescriptor; - } - - return 0; // false - } - - /** - * Determines whether CONSTANT_Methodref_info, - * CONSTANT_Fieldref_info, or - * CONSTANT_InterfaceMethodref_info structure - * at the given index has the name and the descriptor - * given as the arguments. - * - * @param membername the member name - * @param desc the descriptor of the member. - * @param index the index into the constant pool table - * - * @return the name of the target class specified by - * the ..._info structure - * at index. - * Otherwise, null if that structure does not - * match the given member name and descriptor. - */ - public String eqMember(String membername, String desc, int index) { - MemberrefInfo minfo = (MemberrefInfo)getItem(index); - NameAndTypeInfo ntinfo - = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); - if (getUtf8Info(ntinfo.memberName).equals(membername) - && getUtf8Info(ntinfo.typeDescriptor).equals(desc)) - return getClassInfo(minfo.classIndex); - else - return null; // false - } - - private int addItem0(ConstInfo info) { - items.addElement(info); - return numOfItems++; - } - - private int addItem(ConstInfo info) { - if (itemsCache == null) - itemsCache = makeItemsCache(items); - - ConstInfo found = (ConstInfo)itemsCache.get(info); - if (found != null) - return found.index; - else { - items.addElement(info); - itemsCache.put(info, info); - return numOfItems++; - } - } - - /** - * Copies the n-th item in this ConstPool object into the destination - * ConstPool object. - * The class names that the item refers to are renamed according - * to the given map. - * - * @param n the n-th item - * @param dest destination constant pool table - * @param classnames the map or null. - * @return the index of the copied item into the destination ClassPool. - */ - public int copy(int n, ConstPool dest, Map classnames) { - if (n == 0) - return 0; - - ConstInfo info = getItem(n); - return info.copy(this, dest, classnames); - } - - int addConstInfoPadding() { - return addItem0(new ConstInfoPadding(numOfItems)); - } - - /** - * Adds a new CONSTANT_Class_info structure. - * - *

This also adds a CONSTANT_Utf8_info structure - * for storing the class name. - * - * @return the index of the added entry. - */ - public int addClassInfo(CtClass c) { - if (c == THIS) - return thisClassInfo; - else if (!c.isArray()) - return addClassInfo(c.getName()); - else { - // an array type is recorded in the hashtable with - // the key "[L;" instead of "". - // - // note: toJvmName(toJvmName(c)) is equal to toJvmName(c). - - return addClassInfo(Descriptor.toJvmName(c)); - } - } - - /** - * Adds a new CONSTANT_Class_info structure. - * - *

This also adds a CONSTANT_Utf8_info structure - * for storing the class name. - * - * @param qname a fully-qualified class name - * (or the JVM-internal representation of that name). - * @return the index of the added entry. - */ - public int addClassInfo(String qname) { - int utf8 = addUtf8Info(Descriptor.toJvmName(qname)); - return addItem(new ClassInfo(utf8, numOfItems)); - } - - /** - * Adds a new CONSTANT_NameAndType_info structure. - * - *

This also adds CONSTANT_Utf8_info structures. - * - * @param name name_index - * @param type descriptor_index - * @return the index of the added entry. - */ - public int addNameAndTypeInfo(String name, String type) { - return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type)); - } - - /** - * Adds a new CONSTANT_NameAndType_info structure. - * - * @param name name_index - * @param type descriptor_index - * @return the index of the added entry. - */ - public int addNameAndTypeInfo(int name, int type) { - return addItem(new NameAndTypeInfo(name, type, numOfItems)); - } - - /** - * Adds a new CONSTANT_Fieldref_info structure. - * - *

This also adds a new CONSTANT_NameAndType_info - * structure. - * - * @param classInfo class_index - * @param name name_index - * of CONSTANT_NameAndType_info. - * @param type descriptor_index - * of CONSTANT_NameAndType_info. - * @return the index of the added entry. - */ - public int addFieldrefInfo(int classInfo, String name, String type) { - int nt = addNameAndTypeInfo(name, type); - return addFieldrefInfo(classInfo, nt); - } - - /** - * Adds a new CONSTANT_Fieldref_info structure. - * - * @param classInfo class_index - * @param nameAndTypeInfo name_and_type_index. - * @return the index of the added entry. - */ - public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) { - return addItem(new FieldrefInfo(classInfo, nameAndTypeInfo, numOfItems)); - } - - /** - * Adds a new CONSTANT_Methodref_info structure. - * - *

This also adds a new CONSTANT_NameAndType_info - * structure. - * - * @param classInfo class_index - * @param name name_index - * of CONSTANT_NameAndType_info. - * @param type descriptor_index - * of CONSTANT_NameAndType_info. - * @return the index of the added entry. - */ - public int addMethodrefInfo(int classInfo, String name, String type) { - int nt = addNameAndTypeInfo(name, type); - return addMethodrefInfo(classInfo, nt); - } - - /** - * Adds a new CONSTANT_Methodref_info structure. - * - * @param classInfo class_index - * @param nameAndTypeInfo name_and_type_index. - * @return the index of the added entry. - */ - public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) { - return addItem(new MethodrefInfo(classInfo, nameAndTypeInfo, numOfItems)); - } - - /** - * Adds a new CONSTANT_InterfaceMethodref_info - * structure. - * - *

This also adds a new CONSTANT_NameAndType_info - * structure. - * - * @param classInfo class_index - * @param name name_index - * of CONSTANT_NameAndType_info. - * @param type descriptor_index - * of CONSTANT_NameAndType_info. - * @return the index of the added entry. - */ - public int addInterfaceMethodrefInfo(int classInfo, String name, - String type) { - int nt = addNameAndTypeInfo(name, type); - return addInterfaceMethodrefInfo(classInfo, nt); - } - - /** - * Adds a new CONSTANT_InterfaceMethodref_info - * structure. - * - * @param classInfo class_index - * @param nameAndTypeInfo name_and_type_index. - * @return the index of the added entry. - */ - public int addInterfaceMethodrefInfo(int classInfo, - int nameAndTypeInfo) { - return addItem(new InterfaceMethodrefInfo(classInfo, nameAndTypeInfo, - numOfItems)); - } - - /** - * Adds a new CONSTANT_String_info - * structure. - * - *

This also adds a new CONSTANT_Utf8_info - * structure. - * - * @return the index of the added entry. - */ - public int addStringInfo(String str) { - int utf = addUtf8Info(str); - return addItem(new StringInfo(utf, numOfItems)); - } - - /** - * Adds a new CONSTANT_Integer_info - * structure. - * - * @return the index of the added entry. - */ - public int addIntegerInfo(int i) { - return addItem(new IntegerInfo(i, numOfItems)); - } - - /** - * Adds a new CONSTANT_Float_info - * structure. - * - * @return the index of the added entry. - */ - public int addFloatInfo(float f) { - return addItem(new FloatInfo(f, numOfItems)); - } - - /** - * Adds a new CONSTANT_Long_info - * structure. - * - * @return the index of the added entry. - */ - public int addLongInfo(long l) { - int i = addItem(new LongInfo(l, numOfItems)); - if (i == numOfItems - 1) // if not existing - addConstInfoPadding(); - - return i; - } - - /** - * Adds a new CONSTANT_Double_info - * structure. - * - * @return the index of the added entry. - */ - public int addDoubleInfo(double d) { - int i = addItem(new DoubleInfo(d, numOfItems)); - if (i == numOfItems - 1) // if not existing - addConstInfoPadding(); - - return i; - } - - /** - * Adds a new CONSTANT_Utf8_info - * structure. - * - * @return the index of the added entry. - */ - public int addUtf8Info(String utf8) { - return addItem(new Utf8Info(utf8, numOfItems)); - } - - /** - * Adds a new CONSTANT_MethodHandle_info - * structure. - * - * @param kind reference_kind - * such as {@link #REF_invokeStatic REF_invokeStatic}. - * @param index reference_index. - * @return the index of the added entry. - * - * @since 3.17 - */ - public int addMethodHandleInfo(int kind, int index) { - return addItem(new MethodHandleInfo(kind, index, numOfItems)); - } - - /** - * Adds a new CONSTANT_MethodType_info - * structure. - * - * @param desc descriptor_index. - * @return the index of the added entry. - * - * @since 3.17 - */ - public int addMethodTypeInfo(int desc) { - return addItem(new MethodTypeInfo(desc, numOfItems)); - } - - /** - * Adds a new CONSTANT_InvokeDynamic_info - * structure. - * - * @param bootstrap bootstrap_method_attr_index. - * @param nameAndType name_and_type_index. - * @return the index of the added entry. - * - * @since 3.17 - */ - public int addInvokeDynamicInfo(int bootstrap, int nameAndType) { - return addItem(new InvokeDynamicInfo(bootstrap, nameAndType, numOfItems)); - } - - /** - * Get all the class names. - * - * @return a set of class names (String objects). - */ - public Set getClassNames() { - HashSet result = new HashSet(); - LongVector v = items; - int size = numOfItems; - for (int i = 1; i < size; ++i) { - String className = v.elementAt(i).getClassName(this); - if (className != null) - result.add(className); - } - return result; - } - - /** - * Replaces all occurrences of a class name. - * - * @param oldName the replaced name (JVM-internal representation). - * @param newName the substituted name (JVM-internal representation). - */ - public void renameClass(String oldName, String newName) { - LongVector v = items; - int size = numOfItems; - for (int i = 1; i < size; ++i) { - ConstInfo ci = v.elementAt(i); - ci.renameClass(this, oldName, newName, itemsCache); - } - } - - /** - * Replaces all occurrences of class names. - * - * @param classnames specifies pairs of replaced and substituted - * name. - */ - public void renameClass(Map classnames) { - LongVector v = items; - int size = numOfItems; - for (int i = 1; i < size; ++i) { - ConstInfo ci = v.elementAt(i); - ci.renameClass(this, classnames, itemsCache); - } - } - - private void read(DataInputStream in) throws IOException { - int n = in.readUnsignedShort(); - - items = new LongVector(n); - numOfItems = 0; - addItem0(null); // index 0 is reserved by the JVM. - - while (--n > 0) { // index 0 is reserved by JVM - int tag = readOne(in); - if ((tag == LongInfo.tag) || (tag == DoubleInfo.tag)) { - addConstInfoPadding(); - --n; - } - } - } - - private static HashMap makeItemsCache(LongVector items) { - HashMap cache = new HashMap(); - int i = 1; - while (true) { - ConstInfo info = items.elementAt(i++); - if (info == null) - break; - else - cache.put(info, info); - } - - return cache; - } - - private int readOne(DataInputStream in) throws IOException { - ConstInfo info; - int tag = in.readUnsignedByte(); - switch (tag) { - case Utf8Info.tag : // 1 - info = new Utf8Info(in, numOfItems); - break; - case IntegerInfo.tag : // 3 - info = new IntegerInfo(in, numOfItems); - break; - case FloatInfo.tag : // 4 - info = new FloatInfo(in, numOfItems); - break; - case LongInfo.tag : // 5 - info = new LongInfo(in, numOfItems); - break; - case DoubleInfo.tag : // 6 - info = new DoubleInfo(in, numOfItems); - break; - case ClassInfo.tag : // 7 - info = new ClassInfo(in, numOfItems); - break; - case StringInfo.tag : // 8 - info = new StringInfo(in, numOfItems); - break; - case FieldrefInfo.tag : // 9 - info = new FieldrefInfo(in, numOfItems); - break; - case MethodrefInfo.tag : // 10 - info = new MethodrefInfo(in, numOfItems); - break; - case InterfaceMethodrefInfo.tag : // 11 - info = new InterfaceMethodrefInfo(in, numOfItems); - break; - case NameAndTypeInfo.tag : // 12 - info = new NameAndTypeInfo(in, numOfItems); - break; - case MethodHandleInfo.tag : // 15 - info = new MethodHandleInfo(in, numOfItems); - break; - case MethodTypeInfo.tag : // 16 - info = new MethodTypeInfo(in, numOfItems); - break; - case InvokeDynamicInfo.tag : // 18 - info = new InvokeDynamicInfo(in, numOfItems); - break; - default : - throw new IOException("invalid constant type: " + tag + " at " + numOfItems); - } - - addItem0(info); - return tag; - } - - /** - * Writes the contents of the constant pool table. - */ - public void write(DataOutputStream out) throws IOException { - out.writeShort(numOfItems); - LongVector v = items; - int size = numOfItems; - for (int i = 1; i < size; ++i) - v.elementAt(i).write(out); - } - - /** - * Prints the contents of the constant pool table. - */ - public void print() { - print(new PrintWriter(System.out, true)); - } - - /** - * Prints the contents of the constant pool table. - */ - public void print(PrintWriter out) { - int size = numOfItems; - for (int i = 1; i < size; ++i) { - out.print(i); - out.print(" "); - items.elementAt(i).print(out); - } - } -} - -abstract class ConstInfo { - int index; - - public ConstInfo(int i) { index = i; } - - public abstract int getTag(); - - public String getClassName(ConstPool cp) { return null; } - public void renameClass(ConstPool cp, String oldName, String newName, HashMap cache) {} - public void renameClass(ConstPool cp, Map classnames, HashMap cache) {} - public abstract int copy(ConstPool src, ConstPool dest, Map classnames); - // ** classnames is a mapping between JVM names. - - public abstract void write(DataOutputStream out) throws IOException; - public abstract void print(PrintWriter out); - - public String toString() { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - PrintWriter out = new PrintWriter(bout); - print(out); - return bout.toString(); - } -} - -/* padding following DoubleInfo or LongInfo. - */ -class ConstInfoPadding extends ConstInfo { - public ConstInfoPadding(int i) { super(i); } - - public int getTag() { return 0; } - - public int copy(ConstPool src, ConstPool dest, Map map) { - return dest.addConstInfoPadding(); - } - - public void write(DataOutputStream out) throws IOException {} - - public void print(PrintWriter out) { - out.println("padding"); - } -} - -class ClassInfo extends ConstInfo { - static final int tag = 7; - int name; - - public ClassInfo(int className, int index) { - super(index); - name = className; - } - - public ClassInfo(DataInputStream in, int index) throws IOException { - super(index); - name = in.readUnsignedShort(); - } - - public int hashCode() { return name; } - - public boolean equals(Object obj) { - return obj instanceof ClassInfo && ((ClassInfo)obj).name == name; - } - - public int getTag() { return tag; } - - public String getClassName(ConstPool cp) { - return cp.getUtf8Info(name); - } - - public void renameClass(ConstPool cp, String oldName, String newName, HashMap cache) { - String nameStr = cp.getUtf8Info(name); - String newNameStr = null; - if (nameStr.equals(oldName)) - newNameStr = newName; - else if (nameStr.charAt(0) == '[') { - String s = Descriptor.rename(nameStr, oldName, newName); - if (nameStr != s) - newNameStr = s; - } - - if (newNameStr != null) - if (cache == null) - name = cp.addUtf8Info(newNameStr); - else { - cache.remove(this); - name = cp.addUtf8Info(newNameStr); - cache.put(this, this); - } - } - - public void renameClass(ConstPool cp, Map map, HashMap cache) { - String oldName = cp.getUtf8Info(name); - String newName = null; - if (oldName.charAt(0) == '[') { - String s = Descriptor.rename(oldName, map); - if (oldName != s) - newName = s; - } - else { - String s = (String)map.get(oldName); - if (s != null && !s.equals(oldName)) - newName = s; - } - - if (newName != null) { - if (cache == null) - name = cp.addUtf8Info(newName); - else { - cache.remove(this); - name = cp.addUtf8Info(newName); - cache.put(this, this); - } - } - } - - public int copy(ConstPool src, ConstPool dest, Map map) { - String classname = src.getUtf8Info(name); - if (map != null) { - String newname = (String)map.get(classname); - if (newname != null) - classname = newname; - } - - return dest.addClassInfo(classname); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeShort(name); - } - - public void print(PrintWriter out) { - out.print("Class #"); - out.println(name); - } -} - -class NameAndTypeInfo extends ConstInfo { - static final int tag = 12; - int memberName; - int typeDescriptor; - - public NameAndTypeInfo(int name, int type, int index) { - super(index); - memberName = name; - typeDescriptor = type; - } - - public NameAndTypeInfo(DataInputStream in, int index) throws IOException { - super(index); - memberName = in.readUnsignedShort(); - typeDescriptor = in.readUnsignedShort(); - } - - public int hashCode() { return (memberName << 16) ^ typeDescriptor; } - - public boolean equals(Object obj) { - if (obj instanceof NameAndTypeInfo) { - NameAndTypeInfo nti = (NameAndTypeInfo)obj; - return nti.memberName == memberName && nti.typeDescriptor == typeDescriptor; - } - else - return false; - } - - public int getTag() { return tag; } - - public void renameClass(ConstPool cp, String oldName, String newName, HashMap cache) { - String type = cp.getUtf8Info(typeDescriptor); - String type2 = Descriptor.rename(type, oldName, newName); - if (type != type2) - if (cache == null) - typeDescriptor = cp.addUtf8Info(type2); - else { - cache.remove(this); - typeDescriptor = cp.addUtf8Info(type2); - cache.put(this, this); - } - } - - public void renameClass(ConstPool cp, Map map, HashMap cache) { - String type = cp.getUtf8Info(typeDescriptor); - String type2 = Descriptor.rename(type, map); - if (type != type2) - if (cache == null) - typeDescriptor = cp.addUtf8Info(type2); - else { - cache.remove(this); - typeDescriptor = cp.addUtf8Info(type2); - cache.put(this, this); - } - } - - public int copy(ConstPool src, ConstPool dest, Map map) { - String mname = src.getUtf8Info(memberName); - String tdesc = src.getUtf8Info(typeDescriptor); - tdesc = Descriptor.rename(tdesc, map); - return dest.addNameAndTypeInfo(dest.addUtf8Info(mname), - dest.addUtf8Info(tdesc)); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeShort(memberName); - out.writeShort(typeDescriptor); - } - - public void print(PrintWriter out) { - out.print("NameAndType #"); - out.print(memberName); - out.print(", type #"); - out.println(typeDescriptor); - } -} - -abstract class MemberrefInfo extends ConstInfo { - int classIndex; - int nameAndTypeIndex; - - public MemberrefInfo(int cindex, int ntindex, int thisIndex) { - super(thisIndex); - classIndex = cindex; - nameAndTypeIndex = ntindex; - } - - public MemberrefInfo(DataInputStream in, int thisIndex) throws IOException { - super(thisIndex); - classIndex = in.readUnsignedShort(); - nameAndTypeIndex = in.readUnsignedShort(); - } - - public int hashCode() { return (classIndex << 16) ^ nameAndTypeIndex; } - - public boolean equals(Object obj) { - if (obj instanceof MemberrefInfo) { - MemberrefInfo mri = (MemberrefInfo)obj; - return mri.classIndex == classIndex && mri.nameAndTypeIndex == nameAndTypeIndex - && mri.getClass() == this.getClass(); - } - else - return false; - } - - public int copy(ConstPool src, ConstPool dest, Map map) { - int classIndex2 = src.getItem(classIndex).copy(src, dest, map); - int ntIndex2 = src.getItem(nameAndTypeIndex).copy(src, dest, map); - return copy2(dest, classIndex2, ntIndex2); - } - - abstract protected int copy2(ConstPool dest, int cindex, int ntindex); - - public void write(DataOutputStream out) throws IOException { - out.writeByte(getTag()); - out.writeShort(classIndex); - out.writeShort(nameAndTypeIndex); - } - - public void print(PrintWriter out) { - out.print(getTagName() + " #"); - out.print(classIndex); - out.print(", name&type #"); - out.println(nameAndTypeIndex); - } - - public abstract String getTagName(); -} - -class FieldrefInfo extends MemberrefInfo { - static final int tag = 9; - - public FieldrefInfo(int cindex, int ntindex, int thisIndex) { - super(cindex, ntindex, thisIndex); - } - - public FieldrefInfo(DataInputStream in, int thisIndex) throws IOException { - super(in, thisIndex); - } - - public int getTag() { return tag; } - - public String getTagName() { return "Field"; } - - protected int copy2(ConstPool dest, int cindex, int ntindex) { - return dest.addFieldrefInfo(cindex, ntindex); - } -} - -class MethodrefInfo extends MemberrefInfo { - static final int tag = 10; - - public MethodrefInfo(int cindex, int ntindex, int thisIndex) { - super(cindex, ntindex, thisIndex); - } - - public MethodrefInfo(DataInputStream in, int thisIndex) throws IOException { - super(in, thisIndex); - } - - public int getTag() { return tag; } - - public String getTagName() { return "Method"; } - - protected int copy2(ConstPool dest, int cindex, int ntindex) { - return dest.addMethodrefInfo(cindex, ntindex); - } -} - -class InterfaceMethodrefInfo extends MemberrefInfo { - static final int tag = 11; - - public InterfaceMethodrefInfo(int cindex, int ntindex, int thisIndex) { - super(cindex, ntindex, thisIndex); - } - - public InterfaceMethodrefInfo(DataInputStream in, int thisIndex) throws IOException { - super(in, thisIndex); - } - - public int getTag() { return tag; } - - public String getTagName() { return "Interface"; } - - protected int copy2(ConstPool dest, int cindex, int ntindex) { - return dest.addInterfaceMethodrefInfo(cindex, ntindex); - } -} - -class StringInfo extends ConstInfo { - static final int tag = 8; - int string; - - public StringInfo(int str, int index) { - super(index); - string = str; - } - - public StringInfo(DataInputStream in, int index) throws IOException { - super(index); - string = in.readUnsignedShort(); - } - - public int hashCode() { return string; } - - public boolean equals(Object obj) { - return obj instanceof StringInfo && ((StringInfo)obj).string == string; - } - - public int getTag() { return tag; } - - public int copy(ConstPool src, ConstPool dest, Map map) { - return dest.addStringInfo(src.getUtf8Info(string)); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeShort(string); - } - - public void print(PrintWriter out) { - out.print("String #"); - out.println(string); - } -} - -class IntegerInfo extends ConstInfo { - static final int tag = 3; - int value; - - public IntegerInfo(int v, int index) { - super(index); - value = v; - } - - public IntegerInfo(DataInputStream in, int index) throws IOException { - super(index); - value = in.readInt(); - } - - public int hashCode() { return value; } - - public boolean equals(Object obj) { - return obj instanceof IntegerInfo && ((IntegerInfo)obj).value == value; - } - - public int getTag() { return tag; } - - public int copy(ConstPool src, ConstPool dest, Map map) { - return dest.addIntegerInfo(value); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeInt(value); - } - - public void print(PrintWriter out) { - out.print("Integer "); - out.println(value); - } -} - -class FloatInfo extends ConstInfo { - static final int tag = 4; - float value; - - public FloatInfo(float f, int index) { - super(index); - value = f; - } - - public FloatInfo(DataInputStream in, int index) throws IOException { - super(index); - value = in.readFloat(); - } - - public int hashCode() { return Float.floatToIntBits(value); } - - public boolean equals(Object obj) { - return obj instanceof FloatInfo && ((FloatInfo)obj).value == value; - } - - public int getTag() { return tag; } - - public int copy(ConstPool src, ConstPool dest, Map map) { - return dest.addFloatInfo(value); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeFloat(value); - } - - public void print(PrintWriter out) { - out.print("Float "); - out.println(value); - } -} - -class LongInfo extends ConstInfo { - static final int tag = 5; - long value; - - public LongInfo(long l, int index) { - super(index); - value = l; - } - - public LongInfo(DataInputStream in, int index) throws IOException { - super(index); - value = in.readLong(); - } - - public int hashCode() { return (int)(value ^ (value >>> 32)); } - - public boolean equals(Object obj) { - return obj instanceof LongInfo && ((LongInfo)obj).value == value; - } - - public int getTag() { return tag; } - - public int copy(ConstPool src, ConstPool dest, Map map) { - return dest.addLongInfo(value); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeLong(value); - } - - public void print(PrintWriter out) { - out.print("Long "); - out.println(value); - } -} - -class DoubleInfo extends ConstInfo { - static final int tag = 6; - double value; - - public DoubleInfo(double d, int index) { - super(index); - value = d; - } - - public DoubleInfo(DataInputStream in, int index) throws IOException { - super(index); - value = in.readDouble(); - } - - public int hashCode() { - long v = Double.doubleToLongBits(value); - return (int)(v ^ (v >>> 32)); - } - - public boolean equals(Object obj) { - return obj instanceof DoubleInfo && ((DoubleInfo)obj).value == value; - } - - public int getTag() { return tag; } - - public int copy(ConstPool src, ConstPool dest, Map map) { - return dest.addDoubleInfo(value); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeDouble(value); - } - - public void print(PrintWriter out) { - out.print("Double "); - out.println(value); - } -} - -class Utf8Info extends ConstInfo { - static final int tag = 1; - String string; - - public Utf8Info(String utf8, int index) { - super(index); - string = utf8; - } - - public Utf8Info(DataInputStream in, int index) throws IOException { - super(index); - string = in.readUTF(); - } - - public int hashCode() { - return string.hashCode(); - } - - public boolean equals(Object obj) { - return obj instanceof Utf8Info && ((Utf8Info)obj).string.equals(string); - } - - public int getTag() { return tag; } - - public int copy(ConstPool src, ConstPool dest, Map map) { - return dest.addUtf8Info(string); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeUTF(string); - } - - public void print(PrintWriter out) { - out.print("UTF8 \""); - out.print(string); - out.println("\""); - } -} - -class MethodHandleInfo extends ConstInfo { - static final int tag = 15; - int refKind, refIndex; - - public MethodHandleInfo(int kind, int referenceIndex, int index) { - super(index); - refKind = kind; - refIndex = referenceIndex; - } - - public MethodHandleInfo(DataInputStream in, int index) throws IOException { - super(index); - refKind = in.readUnsignedByte(); - refIndex = in.readUnsignedShort(); - } - - public int hashCode() { return (refKind << 16) ^ refIndex; } - - public boolean equals(Object obj) { - if (obj instanceof MethodHandleInfo) { - MethodHandleInfo mh = (MethodHandleInfo)obj; - return mh.refKind == refKind && mh.refIndex == refIndex; - } - else - return false; - } - - public int getTag() { return tag; } - - public int copy(ConstPool src, ConstPool dest, Map map) { - return dest.addMethodHandleInfo(refKind, - src.getItem(refIndex).copy(src, dest, map)); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeByte(refKind); - out.writeShort(refIndex); - } - - public void print(PrintWriter out) { - out.print("MethodHandle #"); - out.print(refKind); - out.print(", index #"); - out.println(refIndex); - } -} - -class MethodTypeInfo extends ConstInfo { - static final int tag = 16; - int descriptor; - - public MethodTypeInfo(int desc, int index) { - super(index); - descriptor = desc; - } - - public MethodTypeInfo(DataInputStream in, int index) throws IOException { - super(index); - descriptor = in.readUnsignedShort(); - } - - public int hashCode() { return descriptor; } - - public boolean equals(Object obj) { - if (obj instanceof MethodTypeInfo) - return ((MethodTypeInfo)obj).descriptor == descriptor; - else - return false; - } - - public int getTag() { return tag; } - - public void renameClass(ConstPool cp, String oldName, String newName, HashMap cache) { - String desc = cp.getUtf8Info(descriptor); - String desc2 = Descriptor.rename(desc, oldName, newName); - if (desc != desc2) - if (cache == null) - descriptor = cp.addUtf8Info(desc2); - else { - cache.remove(this); - descriptor = cp.addUtf8Info(desc2); - cache.put(this, this); - } - } - - public void renameClass(ConstPool cp, Map map, HashMap cache) { - String desc = cp.getUtf8Info(descriptor); - String desc2 = Descriptor.rename(desc, map); - if (desc != desc2) - if (cache == null) - descriptor = cp.addUtf8Info(desc2); - else { - cache.remove(this); - descriptor = cp.addUtf8Info(desc2); - cache.put(this, this); - } - } - - public int copy(ConstPool src, ConstPool dest, Map map) { - String desc = src.getUtf8Info(descriptor); - desc = Descriptor.rename(desc, map); - return dest.addMethodTypeInfo(dest.addUtf8Info(desc)); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeShort(descriptor); - } - - public void print(PrintWriter out) { - out.print("MethodType #"); - out.println(descriptor); - } -} - -class InvokeDynamicInfo extends ConstInfo { - static final int tag = 18; - int bootstrap, nameAndType; - - public InvokeDynamicInfo(int bootstrapMethod, int ntIndex, int index) { - super(index); - bootstrap = bootstrapMethod; - nameAndType = ntIndex; - } - - public InvokeDynamicInfo(DataInputStream in, int index) throws IOException { - super(index); - bootstrap = in.readUnsignedShort(); - nameAndType = in.readUnsignedShort(); - } - - public int hashCode() { return (bootstrap << 16) ^ nameAndType; } - - public boolean equals(Object obj) { - if (obj instanceof InvokeDynamicInfo) { - InvokeDynamicInfo iv = (InvokeDynamicInfo)obj; - return iv.bootstrap == bootstrap && iv.nameAndType == nameAndType; - } - else - return false; - } - - public int getTag() { return tag; } - - public int copy(ConstPool src, ConstPool dest, Map map) { - return dest.addInvokeDynamicInfo(bootstrap, - src.getItem(nameAndType).copy(src, dest, map)); - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeShort(bootstrap); - out.writeShort(nameAndType); - } - - public void print(PrintWriter out) { - out.print("InvokeDynamic #"); - out.print(bootstrap); - out.print(", name&type #"); - out.println(nameAndType); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java deleted file mode 100644 index 900ce1a..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.util.Map; -import java.io.IOException; - -/** - * ConstantValue_attribute. - */ -public class ConstantAttribute extends AttributeInfo { - /** - * The name of this attribute "ConstantValue". - */ - public static final String tag = "ConstantValue"; - - ConstantAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Constructs a ConstantValue attribute. - * - * @param cp a constant pool table. - * @param index constantvalue_index - * of ConstantValue_attribute. - */ - public ConstantAttribute(ConstPool cp, int index) { - super(cp, tag); - byte[] bvalue = new byte[2]; - bvalue[0] = (byte)(index >>> 8); - bvalue[1] = (byte)index; - set(bvalue); - } - - /** - * Returns constantvalue_index. - */ - public int getConstantValue() { - return ByteArray.readU16bit(get(), 0); - } - - /** - * Makes a copy. Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - int index = getConstPool().copy(getConstantValue(), newCp, - classnames); - return new ConstantAttribute(newCp, index); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java deleted file mode 100644 index fdf1a7f..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -/** - * Deprecated_attribute. - */ -public class DeprecatedAttribute extends AttributeInfo { - /** - * The name of this attribute "Deprecated". - */ - public static final String tag = "Deprecated"; - - DeprecatedAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Constructs a Deprecated attribute. - * - * @param cp a constant pool table. - */ - public DeprecatedAttribute(ConstPool cp) { - super(cp, tag, new byte[0]); - } - - /** - * Makes a copy. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames should be null. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - return new DeprecatedAttribute(newCp); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/Descriptor.java b/src/com/wenshuo/agent/javassist/bytecode/Descriptor.java deleted file mode 100644 index 7b8f2f1..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/Descriptor.java +++ /dev/null @@ -1,872 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtPrimitiveType; -import com.wenshuo.agent.javassist.NotFoundException; -import java.util.Map; - -/** - * A support class for dealing with descriptors. - * - *

See chapter 4.3 in "The Java Virtual Machine Specification (2nd ed.)" - */ -public class Descriptor { - /** - * Converts a class name into the internal representation used in - * the JVM. - * - *

Note that toJvmName(toJvmName(s)) is equivalent - * to toJvmName(s). - */ - public static String toJvmName(String classname) { - return classname.replace('.', '/'); - } - - /** - * Converts a class name from the internal representation used in - * the JVM to the normal one used in Java. - * This method does not deal with an array type name such as - * "[Ljava/lang/Object;" and "[I;". For such names, use - * toClassName(). - * - * @see #toClassName(String) - */ - public static String toJavaName(String classname) { - return classname.replace('/', '.'); - } - - /** - * Returns the internal representation of the class name in the - * JVM. - */ - public static String toJvmName(CtClass clazz) { - if (clazz.isArray()) - return of(clazz); - else - return toJvmName(clazz.getName()); - } - - /** - * Converts to a Java class name from a descriptor. - * - * @param descriptor type descriptor. - */ - public static String toClassName(String descriptor) { - int arrayDim = 0; - int i = 0; - char c = descriptor.charAt(0); - while (c == '[') { - ++arrayDim; - c = descriptor.charAt(++i); - } - - String name; - if (c == 'L') { - int i2 = descriptor.indexOf(';', i++); - name = descriptor.substring(i, i2).replace('/', '.'); - i = i2; - } - else if (c == 'V') - name = "void"; - else if (c == 'I') - name = "int"; - else if (c == 'B') - name = "byte"; - else if (c == 'J') - name = "long"; - else if (c == 'D') - name = "double"; - else if (c == 'F') - name = "float"; - else if (c == 'C') - name = "char"; - else if (c == 'S') - name = "short"; - else if (c == 'Z') - name = "boolean"; - else - throw new RuntimeException("bad descriptor: " + descriptor); - - if (i + 1 != descriptor.length()) - throw new RuntimeException("multiple descriptors?: " + descriptor); - - if (arrayDim == 0) - return name; - else { - StringBuffer sbuf = new StringBuffer(name); - do { - sbuf.append("[]"); - } while (--arrayDim > 0); - - return sbuf.toString(); - } - } - - /** - * Converts to a descriptor from a Java class name - */ - public static String of(String classname) { - if (classname.equals("void")) - return "V"; - else if (classname.equals("int")) - return "I"; - else if (classname.equals("byte")) - return "B"; - else if (classname.equals("long")) - return "J"; - else if (classname.equals("double")) - return "D"; - else if (classname.equals("float")) - return "F"; - else if (classname.equals("char")) - return "C"; - else if (classname.equals("short")) - return "S"; - else if (classname.equals("boolean")) - return "Z"; - else - return "L" + toJvmName(classname) + ";"; - } - - /** - * Substitutes a class name - * in the given descriptor string. - * - * @param desc descriptor string - * @param oldname replaced JVM class name - * @param newname substituted JVM class name - * - * @see Descriptor#toJvmName(String) - */ - public static String rename(String desc, String oldname, String newname) { - if (desc.indexOf(oldname) < 0) - return desc; - - StringBuffer newdesc = new StringBuffer(); - int head = 0; - int i = 0; - for (;;) { - int j = desc.indexOf('L', i); - if (j < 0) - break; - else if (desc.startsWith(oldname, j + 1) - && desc.charAt(j + oldname.length() + 1) == ';') { - newdesc.append(desc.substring(head, j)); - newdesc.append('L'); - newdesc.append(newname); - newdesc.append(';'); - head = i = j + oldname.length() + 2; - } - else { - i = desc.indexOf(';', j) + 1; - if (i < 1) - break; // ';' was not found. - } - } - - if (head == 0) - return desc; - else { - int len = desc.length(); - if (head < len) - newdesc.append(desc.substring(head, len)); - - return newdesc.toString(); - } - } - - /** - * Substitutes class names in the given descriptor string - * according to the given map. - * - * @param map a map between replaced and substituted - * JVM class names. - * @see Descriptor#toJvmName(String) - */ - public static String rename(String desc, Map map) { - if (map == null) - return desc; - - StringBuffer newdesc = new StringBuffer(); - int head = 0; - int i = 0; - for (;;) { - int j = desc.indexOf('L', i); - if (j < 0) - break; - - int k = desc.indexOf(';', j); - if (k < 0) - break; - - i = k + 1; - String name = desc.substring(j + 1, k); - String name2 = (String)map.get(name); - if (name2 != null) { - newdesc.append(desc.substring(head, j)); - newdesc.append('L'); - newdesc.append(name2); - newdesc.append(';'); - head = i; - } - } - - if (head == 0) - return desc; - else { - int len = desc.length(); - if (head < len) - newdesc.append(desc.substring(head, len)); - - return newdesc.toString(); - } - } - - /** - * Returns the descriptor representing the given type. - */ - public static String of(CtClass type) { - StringBuffer sbuf = new StringBuffer(); - toDescriptor(sbuf, type); - return sbuf.toString(); - } - - private static void toDescriptor(StringBuffer desc, CtClass type) { - if (type.isArray()) { - desc.append('['); - try { - toDescriptor(desc, type.getComponentType()); - } - catch (NotFoundException e) { - desc.append('L'); - String name = type.getName(); - desc.append(toJvmName(name.substring(0, name.length() - 2))); - desc.append(';'); - } - } - else if (type.isPrimitive()) { - CtPrimitiveType pt = (CtPrimitiveType)type; - desc.append(pt.getDescriptor()); - } - else { // class type - desc.append('L'); - desc.append(type.getName().replace('.', '/')); - desc.append(';'); - } - } - - /** - * Returns the descriptor representing a constructor receiving - * the given parameter types. - * - * @param paramTypes parameter types - */ - public static String ofConstructor(CtClass[] paramTypes) { - return ofMethod(CtClass.voidType, paramTypes); - } - - /** - * Returns the descriptor representing a method that receives - * the given parameter types and returns the given type. - * - * @param returnType return type - * @param paramTypes parameter types - */ - public static String ofMethod(CtClass returnType, CtClass[] paramTypes) { - StringBuffer desc = new StringBuffer(); - desc.append('('); - if (paramTypes != null) { - int n = paramTypes.length; - for (int i = 0; i < n; ++i) - toDescriptor(desc, paramTypes[i]); - } - - desc.append(')'); - if (returnType != null) - toDescriptor(desc, returnType); - - return desc.toString(); - } - - /** - * Returns the descriptor representing a list of parameter types. - * For example, if the given parameter types are two int, - * then this method returns "(II)". - * - * @param paramTypes parameter types - */ - public static String ofParameters(CtClass[] paramTypes) { - return ofMethod(null, paramTypes); - } - - /** - * Appends a parameter type to the parameter list represented - * by the given descriptor. - * - *

classname must not be an array type. - * - * @param classname parameter type (not primitive type) - * @param desc descriptor - */ - public static String appendParameter(String classname, String desc) { - int i = desc.indexOf(')'); - if (i < 0) - return desc; - else { - StringBuffer newdesc = new StringBuffer(); - newdesc.append(desc.substring(0, i)); - newdesc.append('L'); - newdesc.append(classname.replace('.', '/')); - newdesc.append(';'); - newdesc.append(desc.substring(i)); - return newdesc.toString(); - } - } - - /** - * Inserts a parameter type at the beginning of the parameter - * list represented - * by the given descriptor. - * - *

classname must not be an array type. - * - * @param classname parameter type (not primitive type) - * @param desc descriptor - */ - public static String insertParameter(String classname, String desc) { - if (desc.charAt(0) != '(') - return desc; - else - return "(L" + classname.replace('.', '/') + ';' - + desc.substring(1); - } - - /** - * Appends a parameter type to the parameter list represented - * by the given descriptor. The appended parameter becomes - * the last parameter. - * - * @param type the type of the appended parameter. - * @param descriptor the original descriptor. - */ - public static String appendParameter(CtClass type, String descriptor) { - int i = descriptor.indexOf(')'); - if (i < 0) - return descriptor; - else { - StringBuffer newdesc = new StringBuffer(); - newdesc.append(descriptor.substring(0, i)); - toDescriptor(newdesc, type); - newdesc.append(descriptor.substring(i)); - return newdesc.toString(); - } - } - - /** - * Inserts a parameter type at the beginning of the parameter - * list represented - * by the given descriptor. - * - * @param type the type of the inserted parameter. - * @param descriptor the descriptor of the method. - */ - public static String insertParameter(CtClass type, - String descriptor) { - if (descriptor.charAt(0) != '(') - return descriptor; - else - return "(" + of(type) + descriptor.substring(1); - } - - /** - * Changes the return type included in the given descriptor. - * - *

classname must not be an array type. - * - * @param classname return type - * @param desc descriptor - */ - public static String changeReturnType(String classname, String desc) { - int i = desc.indexOf(')'); - if (i < 0) - return desc; - else { - StringBuffer newdesc = new StringBuffer(); - newdesc.append(desc.substring(0, i + 1)); - newdesc.append('L'); - newdesc.append(classname.replace('.', '/')); - newdesc.append(';'); - return newdesc.toString(); - } - } - - /** - * Returns the CtClass objects representing the parameter - * types specified by the given descriptor. - * - * @param desc descriptor - * @param cp the class pool used for obtaining - * a CtClass object. - */ - public static CtClass[] getParameterTypes(String desc, ClassPool cp) - throws NotFoundException - { - if (desc.charAt(0) != '(') - return null; - else { - int num = numOfParameters(desc); - CtClass[] args = new CtClass[num]; - int n = 0; - int i = 1; - do { - i = toCtClass(cp, desc, i, args, n++); - } while (i > 0); - return args; - } - } - - /** - * Returns true if the list of the parameter types of desc1 is equal to - * that of desc2. - * For example, "(II)V" and "(II)I" are equal. - */ - public static boolean eqParamTypes(String desc1, String desc2) { - if (desc1.charAt(0) != '(') - return false; - - for (int i = 0; true; ++i) { - char c = desc1.charAt(i); - if (c != desc2.charAt(i)) - return false; - - if (c == ')') - return true; - } - } - - /** - * Returns the signature of the given descriptor. The signature does - * not include the return type. For example, the signature of "(I)V" - * is "(I)". - */ - public static String getParamDescriptor(String decl) { - return decl.substring(0, decl.indexOf(')') + 1); - } - - /** - * Returns the CtClass object representing the return - * type specified by the given descriptor. - * - * @param desc descriptor - * @param cp the class pool used for obtaining - * a CtClass object. - */ - public static CtClass getReturnType(String desc, ClassPool cp) - throws NotFoundException - { - int i = desc.indexOf(')'); - if (i < 0) - return null; - else { - CtClass[] type = new CtClass[1]; - toCtClass(cp, desc, i + 1, type, 0); - return type[0]; - } - } - - /** - * Returns the number of the prameters included in the given - * descriptor. - * - * @param desc descriptor - */ - public static int numOfParameters(String desc) { - int n = 0; - int i = 1; - for (;;) { - char c = desc.charAt(i); - if (c == ')') - break; - - while (c == '[') - c = desc.charAt(++i); - - if (c == 'L') { - i = desc.indexOf(';', i) + 1; - if (i <= 0) - throw new IndexOutOfBoundsException("bad descriptor"); - } - else - ++i; - - ++n; - } - - return n; - } - - /** - * Returns a CtClass object representing the type - * specified by the given descriptor. - * - *

This method works even if the package-class separator is - * not / but . (period). For example, - * it accepts Ljava.lang.Object; - * as well as Ljava/lang/Object;. - * - * @param desc descriptor. - * @param cp the class pool used for obtaining - * a CtClass object. - */ - public static CtClass toCtClass(String desc, ClassPool cp) - throws NotFoundException - { - CtClass[] clazz = new CtClass[1]; - int res = toCtClass(cp, desc, 0, clazz, 0); - if (res >= 0) - return clazz[0]; - else { - // maybe, you forgot to surround the class name with - // L and ;. It violates the protocol, but I'm tolerant... - return cp.get(desc.replace('/', '.')); - } - } - - private static int toCtClass(ClassPool cp, String desc, int i, - CtClass[] args, int n) - throws NotFoundException - { - int i2; - String name; - - int arrayDim = 0; - char c = desc.charAt(i); - while (c == '[') { - ++arrayDim; - c = desc.charAt(++i); - } - - if (c == 'L') { - i2 = desc.indexOf(';', ++i); - name = desc.substring(i, i2++).replace('/', '.'); - } - else { - CtClass type = toPrimitiveClass(c); - if (type == null) - return -1; // error - - i2 = i + 1; - if (arrayDim == 0) { - args[n] = type; - return i2; // neither an array type or a class type - } - else - name = type.getName(); - } - - if (arrayDim > 0) { - StringBuffer sbuf = new StringBuffer(name); - while (arrayDim-- > 0) - sbuf.append("[]"); - - name = sbuf.toString(); - } - - args[n] = cp.get(name); - return i2; - } - - static CtClass toPrimitiveClass(char c) { - CtClass type = null; - switch (c) { - case 'Z' : - type = CtClass.booleanType; - break; - case 'C' : - type = CtClass.charType; - break; - case 'B' : - type = CtClass.byteType; - break; - case 'S' : - type = CtClass.shortType; - break; - case 'I' : - type = CtClass.intType; - break; - case 'J' : - type = CtClass.longType; - break; - case 'F' : - type = CtClass.floatType; - break; - case 'D' : - type = CtClass.doubleType; - break; - case 'V' : - type = CtClass.voidType; - break; - } - - return type; - } - - /** - * Computes the dimension of the array represented by the given - * descriptor. For example, if the descriptor is "[[I", - * then this method returns 2. - * - * @param desc the descriptor. - * @return 0 if the descriptor does not represent an array type. - */ - public static int arrayDimension(String desc) { - int dim = 0; - while (desc.charAt(dim) == '[') - ++dim; - - return dim; - } - - /** - * Returns the descriptor of the type of the array component. - * For example, if the given descriptor is - * "[[Ljava/lang/String;" and the given dimension is 2, - * then this method returns "Ljava/lang/String;". - * - * @param desc the descriptor. - * @param dim the array dimension. - */ - public static String toArrayComponent(String desc, int dim) { - return desc.substring(dim); - } - - /** - * Computes the data size specified by the given descriptor. - * For example, if the descriptor is "D", this method returns 2. - * - *

If the descriptor represents a method type, this method returns - * (the size of the returned value) - (the sum of the data sizes - * of all the parameters). For example, if the descriptor is - * "(I)D", then this method returns 1 (= 2 - 1). - * - * @param desc descriptor - */ - public static int dataSize(String desc) { - return dataSize(desc, true); - } - - /** - * Computes the data size of parameters. - * If one of the parameters is double type, the size of that parameter - * is 2 words. For example, if the given descriptor is - * "(IJ)D", then this method returns 3. The size of the - * return type is not computed. - * - * @param desc a method descriptor. - */ - public static int paramSize(String desc) { - return -dataSize(desc, false); - } - - private static int dataSize(String desc, boolean withRet) { - int n = 0; - char c = desc.charAt(0); - if (c == '(') { - int i = 1; - for (;;) { - c = desc.charAt(i); - if (c == ')') { - c = desc.charAt(i + 1); - break; - } - - boolean array = false; - while (c == '[') { - array = true; - c = desc.charAt(++i); - } - - if (c == 'L') { - i = desc.indexOf(';', i) + 1; - if (i <= 0) - throw new IndexOutOfBoundsException("bad descriptor"); - } - else - ++i; - - if (!array && (c == 'J' || c == 'D')) - n -= 2; - else - --n; - } - } - - if (withRet) - if (c == 'J' || c == 'D') - n += 2; - else if (c != 'V') - ++n; - - return n; - } - - /** - * Returns a human-readable representation of the - * given descriptor. For example, Ljava/lang/Object; - * is converted into java.lang.Object. - * (I[I)V is converted into (int, int[]) - * (the return type is ignored). - */ - public static String toString(String desc) { - return PrettyPrinter.toString(desc); - } - - static class PrettyPrinter { - static String toString(String desc) { - StringBuffer sbuf = new StringBuffer(); - if (desc.charAt(0) == '(') { - int pos = 1; - sbuf.append('('); - while (desc.charAt(pos) != ')') { - if (pos > 1) - sbuf.append(','); - - pos = readType(sbuf, pos, desc); - } - - sbuf.append(')'); - } - else - readType(sbuf, 0, desc); - - return sbuf.toString(); - } - - static int readType(StringBuffer sbuf, int pos, String desc) { - char c = desc.charAt(pos); - int arrayDim = 0; - while (c == '[') { - arrayDim++; - c = desc.charAt(++pos); - } - - if (c == 'L') - while (true) { - c = desc.charAt(++pos); - if (c == ';') - break; - - if (c == '/') - c = '.'; - - sbuf.append(c); - } - else { - CtClass t = toPrimitiveClass(c); - sbuf.append(t.getName()); - } - - while (arrayDim-- > 0) - sbuf.append("[]"); - - return pos + 1; - } - } - - /** - * An Iterator over a descriptor. - */ - public static class Iterator { - private String desc; - private int index, curPos; - private boolean param; - - /** - * Constructs an iterator. - * - * @param s descriptor. - */ - public Iterator(String s) { - desc = s; - index = curPos = 0; - param = false; - } - - /** - * Returns true if the iteration has more elements. - */ - public boolean hasNext() { - return index < desc.length(); - } - - /** - * Returns true if the current element is a parameter type. - */ - public boolean isParameter() { return param; } - - /** - * Returns the first character of the current element. - */ - public char currentChar() { return desc.charAt(curPos); } - - /** - * Returns true if the current element is double or long type. - */ - public boolean is2byte() { - char c = currentChar(); - return c == 'D' || c == 'J'; - } - - /** - * Returns the position of the next type character. - * That type character becomes a new current element. - */ - public int next() { - int nextPos = index; - char c = desc.charAt(nextPos); - if (c == '(') { - ++index; - c = desc.charAt(++nextPos); - param = true; - } - - if (c == ')') { - ++index; - c = desc.charAt(++nextPos); - param = false; - } - - while (c == '[') - c = desc.charAt(++nextPos); - - if (c == 'L') { - nextPos = desc.indexOf(';', nextPos) + 1; - if (nextPos <= 0) - throw new IndexOutOfBoundsException("bad descriptor"); - } - else - ++nextPos; - - curPos = index; - index = nextPos; - return curPos; - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java b/src/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java deleted file mode 100644 index 1035450..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import com.wenshuo.agent.javassist.CannotCompileException; - -/** - * An exception thrown when adding a duplicate member is requested. - * - * @see ClassFile#addMethod(MethodInfo) - * @see ClassFile#addField(FieldInfo) - */ -public class DuplicateMemberException extends CannotCompileException { - public DuplicateMemberException(String msg) { - super(msg); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java deleted file mode 100644 index 05a0f4a..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -import com.wenshuo.agent.javassist.CtConstructor; - -/** - * EnclosingMethod_attribute. - */ -public class EnclosingMethodAttribute extends AttributeInfo { - /** - * The name of this attribute "EnclosingMethod". - */ - public static final String tag = "EnclosingMethod"; - - EnclosingMethodAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Constructs an EnclosingMethod attribute. - * - * @param cp a constant pool table. - * @param className the name of the innermost enclosing class. - * @param methodName the name of the enclosing method. - * @param methodDesc the descriptor of the enclosing method. - */ - public EnclosingMethodAttribute(ConstPool cp, String className, - String methodName, String methodDesc) { - super(cp, tag); - int ci = cp.addClassInfo(className); - int ni = cp.addNameAndTypeInfo(methodName, methodDesc); - byte[] bvalue = new byte[4]; - bvalue[0] = (byte)(ci >>> 8); - bvalue[1] = (byte)ci; - bvalue[2] = (byte)(ni >>> 8); - bvalue[3] = (byte)ni; - set(bvalue); - } - - /** - * Constructs an EnclosingMethod attribute. - * The value of method_index is set to 0. - * - * @param cp a constant pool table. - * @param className the name of the innermost enclosing class. - */ - public EnclosingMethodAttribute(ConstPool cp, String className) { - super(cp, tag); - int ci = cp.addClassInfo(className); - int ni = 0; - byte[] bvalue = new byte[4]; - bvalue[0] = (byte)(ci >>> 8); - bvalue[1] = (byte)ci; - bvalue[2] = (byte)(ni >>> 8); - bvalue[3] = (byte)ni; - set(bvalue); - } - - /** - * Returns the value of class_index. - */ - public int classIndex() { - return ByteArray.readU16bit(get(), 0); - } - - /** - * Returns the value of method_index. - */ - public int methodIndex() { - return ByteArray.readU16bit(get(), 2); - } - - /** - * Returns the name of the class specified by class_index. - */ - public String className() { - return getConstPool().getClassInfo(classIndex()); - } - - /** - * Returns the method name specified by method_index. - * If the method is a class initializer (static constructor), - * {@link MethodInfo#nameClinit} is returned. - */ - public String methodName() { - ConstPool cp = getConstPool(); - int mi = methodIndex(); - if (mi == 0) - return MethodInfo.nameClinit; - else { - int ni = cp.getNameAndTypeName(mi); - return cp.getUtf8Info(ni); - } - } - - /** - * Returns the method descriptor specified by method_index. - */ - public String methodDescriptor() { - ConstPool cp = getConstPool(); - int mi = methodIndex(); - int ti = cp.getNameAndTypeDescriptor(mi); - return cp.getUtf8Info(ti); - } - - /** - * Makes a copy. Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - if (methodIndex() == 0) - return new EnclosingMethodAttribute(newCp, className()); - else - return new EnclosingMethodAttribute(newCp, className(), - methodName(), methodDescriptor()); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java b/src/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java deleted file mode 100644 index 9ff2fa5..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Map; - -class ExceptionTableEntry { - int startPc; - int endPc; - int handlerPc; - int catchType; - - ExceptionTableEntry(int start, int end, int handle, int type) { - startPc = start; - endPc = end; - handlerPc = handle; - catchType = type; - } -} - -/** - * exception_table[] of Code_attribute. - */ -public class ExceptionTable implements Cloneable { - private ConstPool constPool; - private ArrayList entries; - - /** - * Constructs an exception_table[]. - * - * @param cp constant pool table. - */ - public ExceptionTable(ConstPool cp) { - constPool = cp; - entries = new ArrayList(); - } - - ExceptionTable(ConstPool cp, DataInputStream in) throws IOException { - constPool = cp; - int length = in.readUnsignedShort(); - ArrayList list = new ArrayList(length); - for (int i = 0; i < length; ++i) { - int start = in.readUnsignedShort(); - int end = in.readUnsignedShort(); - int handle = in.readUnsignedShort(); - int type = in.readUnsignedShort(); - list.add(new ExceptionTableEntry(start, end, handle, type)); - } - - entries = list; - } - - /** - * Creates and returns a copy of this object. - * The constant pool object is shared between this object - * and the cloned object. - */ - public Object clone() throws CloneNotSupportedException { - ExceptionTable r = (ExceptionTable)super.clone(); - r.entries = new ArrayList(entries); - return r; - } - - /** - * Returns exception_table_length, which is the number - * of entries in the exception_table[]. - */ - public int size() { - return entries.size(); - } - - /** - * Returns startPc of the n-th entry. - * - * @param nth the n-th (>= 0). - */ - public int startPc(int nth) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); - return e.startPc; - } - - /** - * Sets startPc of the n-th entry. - * - * @param nth the n-th (>= 0). - * @param value new value. - */ - public void setStartPc(int nth, int value) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); - e.startPc = value; - } - - /** - * Returns endPc of the n-th entry. - * - * @param nth the n-th (>= 0). - */ - public int endPc(int nth) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); - return e.endPc; - } - - /** - * Sets endPc of the n-th entry. - * - * @param nth the n-th (>= 0). - * @param value new value. - */ - public void setEndPc(int nth, int value) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); - e.endPc = value; - } - - /** - * Returns handlerPc of the n-th entry. - * - * @param nth the n-th (>= 0). - */ - public int handlerPc(int nth) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); - return e.handlerPc; - } - - /** - * Sets handlerPc of the n-th entry. - * - * @param nth the n-th (>= 0). - * @param value new value. - */ - public void setHandlerPc(int nth, int value) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); - e.handlerPc = value; - } - - /** - * Returns catchType of the n-th entry. - * - * @param nth the n-th (>= 0). - * @return an index into the constant_pool table, - * or zero if this exception handler is for all exceptions. - */ - public int catchType(int nth) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); - return e.catchType; - } - - /** - * Sets catchType of the n-th entry. - * - * @param nth the n-th (>= 0). - * @param value new value. - */ - public void setCatchType(int nth, int value) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); - e.catchType = value; - } - - /** - * Copies the given exception table at the specified position - * in the table. - * - * @param index index (>= 0) at which the entry is to be inserted. - * @param offset the offset added to the code position. - */ - public void add(int index, ExceptionTable table, int offset) { - int len = table.size(); - while (--len >= 0) { - ExceptionTableEntry e - = (ExceptionTableEntry)table.entries.get(len); - add(index, e.startPc + offset, e.endPc + offset, - e.handlerPc + offset, e.catchType); - } - } - - /** - * Adds a new entry at the specified position in the table. - * - * @param index index (>= 0) at which the entry is to be inserted. - * @param start startPc - * @param end endPc - * @param handler handlerPc - * @param type catchType - */ - public void add(int index, int start, int end, int handler, int type) { - if (start < end) - entries.add(index, - new ExceptionTableEntry(start, end, handler, type)); - } - - /** - * Appends a new entry at the end of the table. - * - * @param start startPc - * @param end endPc - * @param handler handlerPc - * @param type catchType - */ - public void add(int start, int end, int handler, int type) { - if (start < end) - entries.add(new ExceptionTableEntry(start, end, handler, type)); - } - - /** - * Removes the entry at the specified position in the table. - * - * @param index the index of the removed entry. - */ - public void remove(int index) { - entries.remove(index); - } - - /** - * Makes a copy of this exception_table[]. - * Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. - */ - public ExceptionTable copy(ConstPool newCp, Map classnames) { - ExceptionTable et = new ExceptionTable(newCp); - ConstPool srcCp = constPool; - int len = size(); - for (int i = 0; i < len; ++i) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i); - int type = srcCp.copy(e.catchType, newCp, classnames); - et.add(e.startPc, e.endPc, e.handlerPc, type); - } - - return et; - } - - void shiftPc(int where, int gapLength, boolean exclusive) { - int len = size(); - for (int i = 0; i < len; ++i) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i); - e.startPc = shiftPc(e.startPc, where, gapLength, exclusive); - e.endPc = shiftPc(e.endPc, where, gapLength, exclusive); - e.handlerPc = shiftPc(e.handlerPc, where, gapLength, exclusive); - } - } - - private static int shiftPc(int pc, int where, int gapLength, - boolean exclusive) { - if (pc > where || (exclusive && pc == where)) - pc += gapLength; - - return pc; - } - - void write(DataOutputStream out) throws IOException { - int len = size(); - out.writeShort(len); // exception_table_length - for (int i = 0; i < len; ++i) { - ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i); - out.writeShort(e.startPc); - out.writeShort(e.endPc); - out.writeShort(e.handlerPc); - out.writeShort(e.catchType); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java deleted file mode 100644 index e20a621..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -/** - * Exceptions_attribute. - */ -public class ExceptionsAttribute extends AttributeInfo { - /** - * The name of this attribute "Exceptions". - */ - public static final String tag = "Exceptions"; - - ExceptionsAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Constructs a copy of an exceptions attribute. - * - * @param cp constant pool table. - * @param src source attribute. - */ - private ExceptionsAttribute(ConstPool cp, ExceptionsAttribute src, - Map classnames) { - super(cp, tag); - copyFrom(src, classnames); - } - - /** - * Constructs a new exceptions attribute. - * - * @param cp constant pool table. - */ - public ExceptionsAttribute(ConstPool cp) { - super(cp, tag); - byte[] data = new byte[2]; - data[0] = data[1] = 0; // empty - this.info = data; - } - - /** - * Makes a copy. Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. It can be null. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - return new ExceptionsAttribute(newCp, this, classnames); - } - - /** - * Copies the contents from a source attribute. - * Specified class names are replaced during the copy. - * - * @param srcAttr source Exceptions attribute - * @param classnames pairs of replaced and substituted - * class names. - */ - private void copyFrom(ExceptionsAttribute srcAttr, Map classnames) { - ConstPool srcCp = srcAttr.constPool; - ConstPool destCp = this.constPool; - byte[] src = srcAttr.info; - int num = src.length; - byte[] dest = new byte[num]; - dest[0] = src[0]; - dest[1] = src[1]; // the number of elements. - for (int i = 2; i < num; i += 2) { - int index = ByteArray.readU16bit(src, i); - ByteArray.write16bit(srcCp.copy(index, destCp, classnames), - dest, i); - } - - this.info = dest; - } - - /** - * Returns exception_index_table[]. - */ - public int[] getExceptionIndexes() { - byte[] blist = info; - int n = blist.length; - if (n <= 2) - return null; - - int[] elist = new int[n / 2 - 1]; - int k = 0; - for (int j = 2; j < n; j += 2) - elist[k++] = ((blist[j] & 0xff) << 8) | (blist[j + 1] & 0xff); - - return elist; - } - - /** - * Returns the names of exceptions that the method may throw. - */ - public String[] getExceptions() { - byte[] blist = info; - int n = blist.length; - if (n <= 2) - return null; - - String[] elist = new String[n / 2 - 1]; - int k = 0; - for (int j = 2; j < n; j += 2) { - int index = ((blist[j] & 0xff) << 8) | (blist[j + 1] & 0xff); - elist[k++] = constPool.getClassInfo(index); - } - - return elist; - } - - /** - * Sets exception_index_table[]. - */ - public void setExceptionIndexes(int[] elist) { - int n = elist.length; - byte[] blist = new byte[n * 2 + 2]; - ByteArray.write16bit(n, blist, 0); - for (int i = 0; i < n; ++i) - ByteArray.write16bit(elist[i], blist, i * 2 + 2); - - info = blist; - } - - /** - * Sets the names of exceptions that the method may throw. - */ - public void setExceptions(String[] elist) { - int n = elist.length; - byte[] blist = new byte[n * 2 + 2]; - ByteArray.write16bit(n, blist, 0); - for (int i = 0; i < n; ++i) - ByteArray.write16bit(constPool.addClassInfo(elist[i]), - blist, i * 2 + 2); - - info = blist; - } - - /** - * Returns number_of_exceptions. - */ - public int tableLength() { return info.length / 2 - 1; } - - /** - * Returns the value of exception_index_table[nth]. - */ - public int getException(int nth) { - int index = nth * 2 + 2; // nth >= 0 - return ((info[index] & 0xff) << 8) | (info[index + 1] & 0xff); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/FieldInfo.java b/src/com/wenshuo/agent/javassist/bytecode/FieldInfo.java deleted file mode 100644 index 62dd9fa..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/FieldInfo.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.List; -import java.util.ArrayList; - -/** - * field_info structure. - * - *

The following code adds a public field width - * of int type: - *

- * ClassFile cf = ...
- * FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I");
- * f.setAccessFlags(AccessFlag.PUBLIC);
- * cf.addField(f);
- * 
- * - * @see javassist.CtField#getFieldInfo() - */ -public final class FieldInfo { - ConstPool constPool; - int accessFlags; - int name; - String cachedName; - String cachedType; - int descriptor; - ArrayList attribute; // may be null. - - private FieldInfo(ConstPool cp) { - constPool = cp; - accessFlags = 0; - attribute = null; - } - - /** - * Constructs a field_info structure. - * - * @param cp a constant pool table - * @param fieldName field name - * @param desc field descriptor - * - * @see Descriptor - */ - public FieldInfo(ConstPool cp, String fieldName, String desc) { - this(cp); - name = cp.addUtf8Info(fieldName); - cachedName = fieldName; - descriptor = cp.addUtf8Info(desc); - } - - FieldInfo(ConstPool cp, DataInputStream in) throws IOException { - this(cp); - read(in); - } - - /** - * Returns a string representation of the object. - */ - public String toString() { - return getName() + " " + getDescriptor(); - } - - /** - * Copies all constant pool items to a given new constant pool - * and replaces the original items with the new ones. - * This is used for garbage collecting the items of removed fields - * and methods. - * - * @param cp the destination - */ - void compact(ConstPool cp) { - name = cp.addUtf8Info(getName()); - descriptor = cp.addUtf8Info(getDescriptor()); - attribute = AttributeInfo.copyAll(attribute, cp); - constPool = cp; - } - - void prune(ConstPool cp) { - ArrayList newAttributes = new ArrayList(); - AttributeInfo invisibleAnnotations - = getAttribute(AnnotationsAttribute.invisibleTag); - if (invisibleAnnotations != null) { - invisibleAnnotations = invisibleAnnotations.copy(cp, null); - newAttributes.add(invisibleAnnotations); - } - - AttributeInfo visibleAnnotations - = getAttribute(AnnotationsAttribute.visibleTag); - if (visibleAnnotations != null) { - visibleAnnotations = visibleAnnotations.copy(cp, null); - newAttributes.add(visibleAnnotations); - } - - AttributeInfo signature - = getAttribute(SignatureAttribute.tag); - if (signature != null) { - signature = signature.copy(cp, null); - newAttributes.add(signature); - } - - int index = getConstantValue(); - if (index != 0) { - index = constPool.copy(index, cp, null); - newAttributes.add(new ConstantAttribute(cp, index)); - } - - attribute = newAttributes; - name = cp.addUtf8Info(getName()); - descriptor = cp.addUtf8Info(getDescriptor()); - constPool = cp; - } - - /** - * Returns the constant pool table used - * by this field_info. - */ - public ConstPool getConstPool() { - return constPool; - } - - /** - * Returns the field name. - */ - public String getName() { - if (cachedName == null) - cachedName = constPool.getUtf8Info(name); - - return cachedName; - } - - /** - * Sets the field name. - */ - public void setName(String newName) { - name = constPool.addUtf8Info(newName); - cachedName = newName; - } - - /** - * Returns the access flags. - * - * @see AccessFlag - */ - public int getAccessFlags() { - return accessFlags; - } - - /** - * Sets the access flags. - * - * @see AccessFlag - */ - public void setAccessFlags(int acc) { - accessFlags = acc; - } - - /** - * Returns the field descriptor. - * - * @see Descriptor - */ - public String getDescriptor() { - return constPool.getUtf8Info(descriptor); - } - - /** - * Sets the field descriptor. - * - * @see Descriptor - */ - public void setDescriptor(String desc) { - if (!desc.equals(getDescriptor())) - descriptor = constPool.addUtf8Info(desc); - } - - /** - * Finds a ConstantValue attribute and returns the index into - * the constant_pool table. - * - * @return 0 if a ConstantValue attribute is not found. - */ - public int getConstantValue() { - if ((accessFlags & AccessFlag.STATIC) == 0) - return 0; - - ConstantAttribute attr - = (ConstantAttribute)getAttribute(ConstantAttribute.tag); - if (attr == null) - return 0; - else - return attr.getConstantValue(); - } - - /** - * Returns all the attributes. The returned List object - * is shared with this object. If you add a new attribute to the list, - * the attribute is also added to the field represented by this - * object. If you remove an attribute from the list, it is also removed - * from the field. - * - * @return a list of AttributeInfo objects. - * @see AttributeInfo - */ - public List getAttributes() { - if (attribute == null) - attribute = new ArrayList(); - - return attribute; - } - - /** - * Returns the attribute with the specified name. - * It returns null if the specified attribute is not found. - * - * @param name attribute name - * @see #getAttributes() - */ - public AttributeInfo getAttribute(String name) { - return AttributeInfo.lookup(attribute, name); - } - - /** - * Appends an attribute. If there is already an attribute with - * the same name, the new one substitutes for it. - * - * @see #getAttributes() - */ - public void addAttribute(AttributeInfo info) { - if (attribute == null) - attribute = new ArrayList(); - - AttributeInfo.remove(attribute, info.getName()); - attribute.add(info); - } - - private void read(DataInputStream in) throws IOException { - accessFlags = in.readUnsignedShort(); - name = in.readUnsignedShort(); - descriptor = in.readUnsignedShort(); - int n = in.readUnsignedShort(); - attribute = new ArrayList(); - for (int i = 0; i < n; ++i) - attribute.add(AttributeInfo.read(constPool, in)); - } - - void write(DataOutputStream out) throws IOException { - out.writeShort(accessFlags); - out.writeShort(name); - out.writeShort(descriptor); - if (attribute == null) - out.writeShort(0); - else { - out.writeShort(attribute.size()); - AttributeInfo.writeAll(attribute, out); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java deleted file mode 100644 index 895c320..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.util.Map; -import java.io.IOException; - -/** - * InnerClasses_attribute. - */ -public class InnerClassesAttribute extends AttributeInfo { - /** - * The name of this attribute "InnerClasses". - */ - public static final String tag = "InnerClasses"; - - InnerClassesAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - private InnerClassesAttribute(ConstPool cp, byte[] info) { - super(cp, tag, info); - } - - /** - * Constructs an empty InnerClasses attribute. - * - * @see #append(String, String, String, int) - */ - public InnerClassesAttribute(ConstPool cp) { - super(cp, tag, new byte[2]); - ByteArray.write16bit(0, get(), 0); - } - - /** - * Returns number_of_classes. - */ - public int tableLength() { return ByteArray.readU16bit(get(), 0); } - - /** - * Returns classes[nth].inner_class_info_index. - */ - public int innerClassIndex(int nth) { - return ByteArray.readU16bit(get(), nth * 8 + 2); - } - - /** - * Returns the class name indicated - * by classes[nth].inner_class_info_index. - * - * @return null or the class name. - */ - public String innerClass(int nth) { - int i = innerClassIndex(nth); - if (i == 0) - return null; - else - return constPool.getClassInfo(i); - } - - /** - * Sets classes[nth].inner_class_info_index to - * the given index. - */ - public void setInnerClassIndex(int nth, int index) { - ByteArray.write16bit(index, get(), nth * 8 + 2); - } - - /** - * Returns classes[nth].outer_class_info_index. - */ - public int outerClassIndex(int nth) { - return ByteArray.readU16bit(get(), nth * 8 + 4); - } - - /** - * Returns the class name indicated - * by classes[nth].outer_class_info_index. - * - * @return null or the class name. - */ - public String outerClass(int nth) { - int i = outerClassIndex(nth); - if (i == 0) - return null; - else - return constPool.getClassInfo(i); - } - - /** - * Sets classes[nth].outer_class_info_index to - * the given index. - */ - public void setOuterClassIndex(int nth, int index) { - ByteArray.write16bit(index, get(), nth * 8 + 4); - } - - /** - * Returns classes[nth].inner_name_index. - */ - public int innerNameIndex(int nth) { - return ByteArray.readU16bit(get(), nth * 8 + 6); - } - - /** - * Returns the simple class name indicated - * by classes[nth].inner_name_index. - * - * @return null or the class name. - */ - public String innerName(int nth) { - int i = innerNameIndex(nth); - if (i == 0) - return null; - else - return constPool.getUtf8Info(i); - } - - /** - * Sets classes[nth].inner_name_index to - * the given index. - */ - public void setInnerNameIndex(int nth, int index) { - ByteArray.write16bit(index, get(), nth * 8 + 6); - } - - /** - * Returns classes[nth].inner_class_access_flags. - */ - public int accessFlags(int nth) { - return ByteArray.readU16bit(get(), nth * 8 + 8); - } - - /** - * Sets classes[nth].inner_class_access_flags to - * the given index. - */ - public void setAccessFlags(int nth, int flags) { - ByteArray.write16bit(flags, get(), nth * 8 + 8); - } - - /** - * Appends a new entry. - * - * @param inner inner_class_info_index - * @param outer outer_class_info_index - * @param name inner_name_index - * @param flags inner_class_access_flags - */ - public void append(String inner, String outer, String name, int flags) { - int i = constPool.addClassInfo(inner); - int o = constPool.addClassInfo(outer); - int n = constPool.addUtf8Info(name); - append(i, o, n, flags); - } - - /** - * Appends a new entry. - * - * @param inner inner_class_info_index - * @param outer outer_class_info_index - * @param name inner_name_index - * @param flags inner_class_access_flags - */ - public void append(int inner, int outer, int name, int flags) { - byte[] data = get(); - int len = data.length; - byte[] newData = new byte[len + 8]; - for (int i = 2; i < len; ++i) - newData[i] = data[i]; - - int n = ByteArray.readU16bit(data, 0); - ByteArray.write16bit(n + 1, newData, 0); - - ByteArray.write16bit(inner, newData, len); - ByteArray.write16bit(outer, newData, len + 2); - ByteArray.write16bit(name, newData, len + 4); - ByteArray.write16bit(flags, newData, len + 6); - - set(newData); - } - - /** - * Makes a copy. Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - byte[] src = get(); - byte[] dest = new byte[src.length]; - ConstPool cp = getConstPool(); - InnerClassesAttribute attr = new InnerClassesAttribute(newCp, dest); - int n = ByteArray.readU16bit(src, 0); - ByteArray.write16bit(n, dest, 0); - int j = 2; - for (int i = 0; i < n; ++i) { - int innerClass = ByteArray.readU16bit(src, j); - int outerClass = ByteArray.readU16bit(src, j + 2); - int innerName = ByteArray.readU16bit(src, j + 4); - int innerAccess = ByteArray.readU16bit(src, j + 6); - - if (innerClass != 0) - innerClass = cp.copy(innerClass, newCp, classnames); - - ByteArray.write16bit(innerClass, dest, j); - - if (outerClass != 0) - outerClass = cp.copy(outerClass, newCp, classnames); - - ByteArray.write16bit(outerClass, dest, j + 2); - - if (innerName != 0) - innerName = cp.copy(innerName, newCp, classnames); - - ByteArray.write16bit(innerName, dest, j + 4); - ByteArray.write16bit(innerAccess, dest, j + 6); - j += 8; - } - - return attr; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java b/src/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java deleted file mode 100644 index c9cc815..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode; - -import java.io.PrintStream; - -import com.wenshuo.agent.javassist.CtMethod; - -/** - * Simple utility class for printing the bytecode instructions of a method. - * - * @author Jason T. Greene - */ -public class InstructionPrinter implements Opcode { - - private final static String opcodes[] = Mnemonic.OPCODE; - private final PrintStream stream; - - /** - * Constructs a InstructionPrinter object. - */ - public InstructionPrinter(PrintStream stream) { - this.stream = stream; - } - - /** - * Prints the bytecode instructions of a given method. - */ - public static void print(CtMethod method, PrintStream stream) { - (new InstructionPrinter(stream)).print(method); - } - - /** - * Prints the bytecode instructions of a given method. - */ - public void print(CtMethod method) { - MethodInfo info = method.getMethodInfo2(); - ConstPool pool = info.getConstPool(); - CodeAttribute code = info.getCodeAttribute(); - if (code == null) - return; - - CodeIterator iterator = code.iterator(); - while (iterator.hasNext()) { - int pos; - try { - pos = iterator.next(); - } catch (BadBytecode e) { - throw new RuntimeException(e); - } - - stream.println(pos + ": " + instructionString(iterator, pos, pool)); - } - } - - /** - * Gets a string representation of the bytecode instruction at the specified - * position. - */ - public static String instructionString(CodeIterator iter, int pos, ConstPool pool) { - int opcode = iter.byteAt(pos); - - if (opcode > opcodes.length || opcode < 0) - throw new IllegalArgumentException("Invalid opcode, opcode: " + opcode + " pos: "+ pos); - - String opstring = opcodes[opcode]; - switch (opcode) { - case BIPUSH: - return opstring + " " + iter.byteAt(pos + 1); - case SIPUSH: - return opstring + " " + iter.s16bitAt(pos + 1); - case LDC: - return opstring + " " + ldc(pool, iter.byteAt(pos + 1)); - case LDC_W : - case LDC2_W : - return opstring + " " + ldc(pool, iter.u16bitAt(pos + 1)); - case ILOAD: - case LLOAD: - case FLOAD: - case DLOAD: - case ALOAD: - case ISTORE: - case LSTORE: - case FSTORE: - case DSTORE: - case ASTORE: - return opstring + " " + iter.byteAt(pos + 1); - case IFEQ: - case IFGE: - case IFGT: - case IFLE: - case IFLT: - case IFNE: - case IFNONNULL: - case IFNULL: - case IF_ACMPEQ: - case IF_ACMPNE: - case IF_ICMPEQ: - case IF_ICMPGE: - case IF_ICMPGT: - case IF_ICMPLE: - case IF_ICMPLT: - case IF_ICMPNE: - return opstring + " " + (iter.s16bitAt(pos + 1) + pos); - case IINC: - return opstring + " " + iter.byteAt(pos + 1) + ", " + iter.signedByteAt(pos + 2); - case GOTO: - case JSR: - return opstring + " " + (iter.s16bitAt(pos + 1) + pos); - case RET: - return opstring + " " + iter.byteAt(pos + 1); - case TABLESWITCH: - return tableSwitch(iter, pos); - case LOOKUPSWITCH: - return lookupSwitch(iter, pos); - case GETSTATIC: - case PUTSTATIC: - case GETFIELD: - case PUTFIELD: - return opstring + " " + fieldInfo(pool, iter.u16bitAt(pos + 1)); - case INVOKEVIRTUAL: - case INVOKESPECIAL: - case INVOKESTATIC: - return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1)); - case INVOKEINTERFACE: - return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1)); - case INVOKEDYNAMIC: - return opstring + " " + iter.u16bitAt(pos + 1); - case NEW: - return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); - case NEWARRAY: - return opstring + " " + arrayInfo(iter.byteAt(pos + 1)); - case ANEWARRAY: - case CHECKCAST: - return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); - case WIDE: - return wide(iter, pos); - case MULTIANEWARRAY: - return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); - case GOTO_W: - case JSR_W: - return opstring + " " + (iter.s32bitAt(pos + 1)+ pos); - default: - return opstring; - } - } - - - private static String wide(CodeIterator iter, int pos) { - int opcode = iter.byteAt(pos + 1); - int index = iter.u16bitAt(pos + 2); - switch (opcode) { - case ILOAD: - case LLOAD: - case FLOAD: - case DLOAD: - case ALOAD: - case ISTORE: - case LSTORE: - case FSTORE: - case DSTORE: - case ASTORE: - case IINC: - case RET: - return opcodes[opcode] + " " + index; - default: - throw new RuntimeException("Invalid WIDE operand"); - } - } - - - private static String arrayInfo(int type) { - switch (type) { - case T_BOOLEAN: - return "boolean"; - case T_CHAR: - return "char"; - case T_BYTE: - return "byte"; - case T_SHORT: - return "short"; - case T_INT: - return "int"; - case T_LONG: - return "long"; - case T_FLOAT: - return "float"; - case T_DOUBLE: - return "double"; - default: - throw new RuntimeException("Invalid array type"); - } - } - - - private static String classInfo(ConstPool pool, int index) { - return "#" + index + " = Class " + pool.getClassInfo(index); - } - - - private static String interfaceMethodInfo(ConstPool pool, int index) { - return "#" + index + " = Method " - + pool.getInterfaceMethodrefClassName(index) + "." - + pool.getInterfaceMethodrefName(index) + "(" - + pool.getInterfaceMethodrefType(index) + ")"; - } - - private static String methodInfo(ConstPool pool, int index) { - return "#" + index + " = Method " - + pool.getMethodrefClassName(index) + "." - + pool.getMethodrefName(index) + "(" - + pool.getMethodrefType(index) + ")"; - } - - - private static String fieldInfo(ConstPool pool, int index) { - return "#" + index + " = Field " - + pool.getFieldrefClassName(index) + "." - + pool.getFieldrefName(index) + "(" - + pool.getFieldrefType(index) + ")"; - } - - - private static String lookupSwitch(CodeIterator iter, int pos) { - StringBuffer buffer = new StringBuffer("lookupswitch {\n"); - int index = (pos & ~3) + 4; - // default - buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); - int npairs = iter.s32bitAt(index += 4); - int end = npairs * 8 + (index += 4); - - for (; index < end; index += 8) { - int match = iter.s32bitAt(index); - int target = iter.s32bitAt(index + 4) + pos; - buffer.append("\t\t").append(match).append(": ").append(target).append("\n"); - } - - buffer.setCharAt(buffer.length() - 1, '}'); - return buffer.toString(); - } - - - private static String tableSwitch(CodeIterator iter, int pos) { - StringBuffer buffer = new StringBuffer("tableswitch {\n"); - int index = (pos & ~3) + 4; - // default - buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); - int low = iter.s32bitAt(index += 4); - int high = iter.s32bitAt(index += 4); - int end = (high - low + 1) * 4 + (index += 4); - - // Offset table - for (int key = low; index < end; index += 4, key++) { - int target = iter.s32bitAt(index) + pos; - buffer.append("\t\t").append(key).append(": ").append(target).append("\n"); - } - - buffer.setCharAt(buffer.length() - 1, '}'); - return buffer.toString(); - } - - - private static String ldc(ConstPool pool, int index) { - int tag = pool.getTag(index); - switch (tag) { - case ConstPool.CONST_String: - return "#" + index + " = \"" + pool.getStringInfo(index) + "\""; - case ConstPool.CONST_Integer: - return "#" + index + " = int " + pool.getIntegerInfo(index); - case ConstPool.CONST_Float: - return "#" + index + " = float " + pool.getFloatInfo(index); - case ConstPool.CONST_Long: - return "#" + index + " = long " + pool.getLongInfo(index); - case ConstPool.CONST_Double: - return "#" + index + " = int " + pool.getDoubleInfo(index); - case ConstPool.CONST_Class: - return classInfo(pool, index); - default: - throw new RuntimeException("bad LDC: " + tag); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java deleted file mode 100644 index c8a9dcc..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -/** - * LineNumberTable_attribute. - */ -public class LineNumberAttribute extends AttributeInfo { - /** - * The name of this attribute "LineNumberTable". - */ - public static final String tag = "LineNumberTable"; - - LineNumberAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - private LineNumberAttribute(ConstPool cp, byte[] i) { - super(cp, tag, i); - } - - /** - * Returns line_number_table_length. - * This represents the number of entries in the table. - */ - public int tableLength() { - return ByteArray.readU16bit(info, 0); - } - - /** - * Returns line_number_table[i].start_pc. - * This represents the index into the code array at which the code - * for a new line in the original source file begins. - * - * @param i the i-th entry. - */ - public int startPc(int i) { - return ByteArray.readU16bit(info, i * 4 + 2); - } - - /** - * Returns line_number_table[i].line_number. - * This represents the corresponding line number in the original - * source file. - * - * @param i the i-th entry. - */ - public int lineNumber(int i) { - return ByteArray.readU16bit(info, i * 4 + 4); - } - - /** - * Returns the line number corresponding to the specified bytecode. - * - * @param pc the index into the code array. - */ - public int toLineNumber(int pc) { - int n = tableLength(); - int i = 0; - for (; i < n; ++i) - if (pc < startPc(i)) - if (i == 0) - return lineNumber(0); - else - break; - - return lineNumber(i - 1); - } - - /** - * Returns the index into the code array at which the code for - * the specified line begins. - * - * @param line the line number. - * @return -1 if the specified line is not found. - */ - public int toStartPc(int line) { - int n = tableLength(); - for (int i = 0; i < n; ++i) - if (line == lineNumber(i)) - return startPc(i); - - return -1; - } - - /** - * Used as a return type of toNearPc(). - */ - static public class Pc { - /** - * The index into the code array. - */ - public int index; - /** - * The line number. - */ - public int line; - } - - /** - * Returns the index into the code array at which the code for - * the specified line (or the nearest line after the specified one) - * begins. - * - * @param line the line number. - * @return a pair of the index and the line number of the - * bytecode at that index. - */ - public Pc toNearPc(int line) { - int n = tableLength(); - int nearPc = 0; - int distance = 0; - if (n > 0) { - distance = lineNumber(0) - line; - nearPc = startPc(0); - } - - for (int i = 1; i < n; ++i) { - int d = lineNumber(i) - line; - if ((d < 0 && d > distance) - || (d >= 0 && (d < distance || distance < 0))) { - distance = d; - nearPc = startPc(i); - } - } - - Pc res = new Pc(); - res.index = nearPc; - res.line = line + distance; - return res; - } - - /** - * Makes a copy. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames should be null. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - byte[] src = info; - int num = src.length; - byte[] dest = new byte[num]; - for (int i = 0; i < num; ++i) - dest[i] = src[i]; - - LineNumberAttribute attr = new LineNumberAttribute(newCp, dest); - return attr; - } - - /** - * Adjusts start_pc if bytecode is inserted in a method body. - */ - void shiftPc(int where, int gapLength, boolean exclusive) { - int n = tableLength(); - for (int i = 0; i < n; ++i) { - int pos = i * 4 + 2; - int pc = ByteArray.readU16bit(info, pos); - if (pc > where || (exclusive && pc == where)) - ByteArray.write16bit(pc + gapLength, info, pos); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java deleted file mode 100644 index 8ce0ccd..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -/** - * LocalVariableTable_attribute. - */ -public class LocalVariableAttribute extends AttributeInfo { - /** - * The name of this attribute "LocalVariableTable". - */ - public static final String tag = "LocalVariableTable"; - - /** - * The name of the attribute "LocalVariableTypeTable". - */ - public static final String typeTag = "LocalVariableTypeTable"; - - /** - * Constructs an empty LocalVariableTable. - */ - public LocalVariableAttribute(ConstPool cp) { - super(cp, tag, new byte[2]); - ByteArray.write16bit(0, info, 0); - } - - /** - * Constructs an empty LocalVariableTable. - * - * @param name the attribute name. - * LocalVariableAttribute.tag or - * LocalVariableAttribute.typeTag. - * @see #tag - * @see #typeTag - * @since 3.1 - * @deprecated - */ - public LocalVariableAttribute(ConstPool cp, String name) { - super(cp, name, new byte[2]); - ByteArray.write16bit(0, info, 0); - } - - LocalVariableAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - LocalVariableAttribute(ConstPool cp, String name, byte[] i) { - super(cp, name, i); - } - - /** - * Appends a new entry to local_variable_table. - * - * @param startPc start_pc - * @param length length - * @param nameIndex name_index - * @param descriptorIndex descriptor_index - * @param index index - */ - public void addEntry(int startPc, int length, int nameIndex, - int descriptorIndex, int index) { - int size = info.length; - byte[] newInfo = new byte[size + 10]; - ByteArray.write16bit(tableLength() + 1, newInfo, 0); - for (int i = 2; i < size; ++i) - newInfo[i] = info[i]; - - ByteArray.write16bit(startPc, newInfo, size); - ByteArray.write16bit(length, newInfo, size + 2); - ByteArray.write16bit(nameIndex, newInfo, size + 4); - ByteArray.write16bit(descriptorIndex, newInfo, size + 6); - ByteArray.write16bit(index, newInfo, size + 8); - info = newInfo; - } - - void renameClass(String oldname, String newname) { - ConstPool cp = getConstPool(); - int n = tableLength(); - for (int i = 0; i < n; ++i) { - int pos = i * 10 + 2; - int index = ByteArray.readU16bit(info, pos + 6); - if (index != 0) { - String desc = cp.getUtf8Info(index); - desc = renameEntry(desc, oldname, newname); - ByteArray.write16bit(cp.addUtf8Info(desc), info, pos + 6); - } - } - } - - String renameEntry(String desc, String oldname, String newname) { - return Descriptor.rename(desc, oldname, newname); - } - - void renameClass(Map classnames) { - ConstPool cp = getConstPool(); - int n = tableLength(); - for (int i = 0; i < n; ++i) { - int pos = i * 10 + 2; - int index = ByteArray.readU16bit(info, pos + 6); - if (index != 0) { - String desc = cp.getUtf8Info(index); - desc = renameEntry(desc, classnames); - ByteArray.write16bit(cp.addUtf8Info(desc), info, pos + 6); - } - } - } - - String renameEntry(String desc, Map classnames) { - return Descriptor.rename(desc, classnames); - } - - /** - * For each local_variable_table[i].index, - * this method increases index by delta. - * - * @param lessThan the index does not change if it - * is less than this value. - */ - public void shiftIndex(int lessThan, int delta) { - int size = info.length; - for (int i = 2; i < size; i += 10){ - int org = ByteArray.readU16bit(info, i + 8); - if (org >= lessThan) - ByteArray.write16bit(org + delta, info, i + 8); - } - } - - /** - * Returns local_variable_table_length. - * This represents the number of entries in the table. - */ - public int tableLength() { - return ByteArray.readU16bit(info, 0); - } - - /** - * Returns local_variable_table[i].start_pc. - * This represents the index into the code array from which the local - * variable is effective. - * - * @param i the i-th entry. - */ - public int startPc(int i) { - return ByteArray.readU16bit(info, i * 10 + 2); - } - - /** - * Returns local_variable_table[i].length. - * This represents the length of the code region in which the local - * variable is effective. - * - * @param i the i-th entry. - */ - public int codeLength(int i) { - return ByteArray.readU16bit(info, i * 10 + 4); - } - - /** - * Adjusts start_pc and length if bytecode is inserted in a method body. - */ - void shiftPc(int where, int gapLength, boolean exclusive) { - int n = tableLength(); - for (int i = 0; i < n; ++i) { - int pos = i * 10 + 2; - int pc = ByteArray.readU16bit(info, pos); - int len = ByteArray.readU16bit(info, pos + 2); - - /* if pc == 0, then the local variable is a method parameter. - */ - if (pc > where || (exclusive && pc == where && pc != 0)) - ByteArray.write16bit(pc + gapLength, info, pos); - else if (pc + len > where || (exclusive && pc + len == where)) - ByteArray.write16bit(len + gapLength, info, pos + 2); - } - } - - /** - * Returns the value of local_variable_table[i].name_index. - * This represents the name of the local variable. - * - * @param i the i-th entry. - */ - public int nameIndex(int i) { - return ByteArray.readU16bit(info, i * 10 + 6); - } - - /** - * Returns the name of the local variable - * specified by local_variable_table[i].name_index. - * - * @param i the i-th entry. - */ - public String variableName(int i) { - return getConstPool().getUtf8Info(nameIndex(i)); - } - - /** - * Returns the value of - * local_variable_table[i].descriptor_index. - * This represents the type descriptor of the local variable. - *

- * If this attribute represents a LocalVariableTypeTable attribute, - * this method returns the value of - * local_variable_type_table[i].signature_index. - * It represents the type of the local variable. - * - * @param i the i-th entry. - */ - public int descriptorIndex(int i) { - return ByteArray.readU16bit(info, i * 10 + 8); - } - - /** - * This method is equivalent to descriptorIndex(). - * If this attribute represents a LocalVariableTypeTable attribute, - * this method should be used instead of descriptorIndex() - * since the method name is more appropriate. - * - * @param i the i-th entry. - * @see #descriptorIndex(int) - * @see SignatureAttribute#toFieldSignature(String) - */ - public int signatureIndex(int i) { - return descriptorIndex(i); - } - - /** - * Returns the type descriptor of the local variable - * specified by local_variable_table[i].descriptor_index. - *

- * If this attribute represents a LocalVariableTypeTable attribute, - * this method returns the type signature of the local variable - * specified by local_variable_type_table[i].signature_index. - * - * @param i the i-th entry. - */ - public String descriptor(int i) { - return getConstPool().getUtf8Info(descriptorIndex(i)); - } - - /** - * This method is equivalent to descriptor(). - * If this attribute represents a LocalVariableTypeTable attribute, - * this method should be used instead of descriptor() - * since the method name is more appropriate. - * - *

To parse the string, call toFieldSignature(String) - * in SignatureAttribute. - * - * @param i the i-th entry. - * @see #descriptor(int) - * @see SignatureAttribute#toFieldSignature(String) - */ - public String signature(int i) { - return descriptor(i); - } - - /** - * Returns local_variable_table[i].index. - * This represents the index of the local variable. - * - * @param i the i-th entry. - */ - public int index(int i) { - return ByteArray.readU16bit(info, i * 10 + 10); - } - - /** - * Makes a copy. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames should be null. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - byte[] src = get(); - byte[] dest = new byte[src.length]; - ConstPool cp = getConstPool(); - LocalVariableAttribute attr = makeThisAttr(newCp, dest); - int n = ByteArray.readU16bit(src, 0); - ByteArray.write16bit(n, dest, 0); - int j = 2; - for (int i = 0; i < n; ++i) { - int start = ByteArray.readU16bit(src, j); - int len = ByteArray.readU16bit(src, j + 2); - int name = ByteArray.readU16bit(src, j + 4); - int type = ByteArray.readU16bit(src, j + 6); - int index = ByteArray.readU16bit(src, j + 8); - - ByteArray.write16bit(start, dest, j); - ByteArray.write16bit(len, dest, j + 2); - if (name != 0) - name = cp.copy(name, newCp, null); - - ByteArray.write16bit(name, dest, j + 4); - - if (type != 0) { - String sig = cp.getUtf8Info(type); - sig = Descriptor.rename(sig, classnames); - type = newCp.addUtf8Info(sig); - } - - ByteArray.write16bit(type, dest, j + 6); - ByteArray.write16bit(index, dest, j + 8); - j += 10; - } - - return attr; - } - - // LocalVariableTypeAttribute overrides this method. - LocalVariableAttribute makeThisAttr(ConstPool cp, byte[] dest) { - return new LocalVariableAttribute(cp, tag, dest); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java deleted file mode 100644 index 57bd395..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -/** - * LocalVariableTypeTable_attribute. - * - * @since 3.11 - */ -public class LocalVariableTypeAttribute extends LocalVariableAttribute { - /** - * The name of the attribute "LocalVariableTypeTable". - */ - public static final String tag = LocalVariableAttribute.typeTag; - - /** - * Constructs an empty LocalVariableTypeTable. - */ - public LocalVariableTypeAttribute(ConstPool cp) { - super(cp, tag, new byte[2]); - ByteArray.write16bit(0, info, 0); - } - - LocalVariableTypeAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - private LocalVariableTypeAttribute(ConstPool cp, byte[] dest) { - super(cp, tag, dest); - } - - String renameEntry(String desc, String oldname, String newname) { - return SignatureAttribute.renameClass(desc, oldname, newname); - } - - String renameEntry(String desc, Map classnames) { - return SignatureAttribute.renameClass(desc, classnames); - } - - LocalVariableAttribute makeThisAttr(ConstPool cp, byte[] dest) { - return new LocalVariableTypeAttribute(cp, dest); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/LongVector.java b/src/com/wenshuo/agent/javassist/bytecode/LongVector.java deleted file mode 100644 index 16f40e2..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/LongVector.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -final class LongVector { - static final int ASIZE = 128; - static final int ABITS = 7; // ASIZE = 2^ABITS - static final int VSIZE = 8; - private ConstInfo[][] objects; - private int elements; - - public LongVector() { - objects = new ConstInfo[VSIZE][]; - elements = 0; - } - - public LongVector(int initialSize) { - int vsize = ((initialSize >> ABITS) & ~(VSIZE - 1)) + VSIZE; - objects = new ConstInfo[vsize][]; - elements = 0; - } - - public int size() { return elements; } - - public int capacity() { return objects.length * ASIZE; } - - public ConstInfo elementAt(int i) { - if (i < 0 || elements <= i) - return null; - - return objects[i >> ABITS][i & (ASIZE - 1)]; - } - - public void addElement(ConstInfo value) { - int nth = elements >> ABITS; - int offset = elements & (ASIZE - 1); - int len = objects.length; - if (nth >= len) { - ConstInfo[][] newObj = new ConstInfo[len + VSIZE][]; - System.arraycopy(objects, 0, newObj, 0, len); - objects = newObj; - } - - if (objects[nth] == null) - objects[nth] = new ConstInfo[ASIZE]; - - objects[nth][offset] = value; - elements++; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/MethodInfo.java b/src/com/wenshuo/agent/javassist/bytecode/MethodInfo.java deleted file mode 100644 index dbe32ca..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/MethodInfo.java +++ /dev/null @@ -1,566 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.stackmap.MapMaker; - -/** - * method_info structure. - * - *

The bytecode sequence of the method is represented - * by a CodeAttribute object. - * - *

The following code adds the default constructor to a class: - * of int type: - *

- * ClassFile cf = ...
- * Bytecode code = new Bytecode(cf.getConstPool());
- * code.addAload(0);
- * code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V");
- * code.addReturn(null);
- * code.setMaxLocals(1);
- *
- * MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V");
- * minfo.setCodeAttribute(code.toCodeAttribute());
- * cf.addMethod(minfo);
- * 
- * - * @see #getCodeAttribute() - * @see CodeAttribute - * @see Bytecode - * @see javassist.CtMethod#getMethodInfo() - * @see javassist.CtConstructor#getMethodInfo() - */ -public class MethodInfo { - ConstPool constPool; - int accessFlags; - int name; - String cachedName; - int descriptor; - ArrayList attribute; // may be null - - /** - * If this value is true, Javassist maintains a StackMap attribute - * generated by the preverify tool of J2ME (CLDC). The initial - * value of this field is false. - */ - public static boolean doPreverify = false; - - /** - * The name of constructors: <init>. - */ - public static final String nameInit = ""; - - /** - * The name of class initializer (static initializer): - * <clinit>. - */ - public static final String nameClinit = ""; - - private MethodInfo(ConstPool cp) { - constPool = cp; - attribute = null; - } - - /** - * Constructs a method_info structure. The initial value of - * access_flags is zero. - * - * @param cp - * a constant pool table - * @param methodname - * method name - * @param desc - * method descriptor - * @see Descriptor - */ - public MethodInfo(ConstPool cp, String methodname, String desc) { - this(cp); - accessFlags = 0; - name = cp.addUtf8Info(methodname); - cachedName = methodname; - descriptor = constPool.addUtf8Info(desc); - } - - MethodInfo(ConstPool cp, DataInputStream in) throws IOException { - this(cp); - read(in); - } - - /** - * Constructs a copy of method_info structure. Class names - * appearing in the source method_info are renamed according - * to classnameMap. - * - *

- * Note: only Code and Exceptions attributes - * are copied from the source. The other attributes are ignored. - * - * @param cp - * a constant pool table - * @param methodname - * a method name - * @param src - * a source method_info - * @param classnameMap - * specifies pairs of replaced and substituted name. - * @see Descriptor - */ - public MethodInfo(ConstPool cp, String methodname, MethodInfo src, - Map classnameMap) throws BadBytecode { - this(cp); - read(src, methodname, classnameMap); - } - - /** - * Returns a string representation of the object. - */ - public String toString() { - return getName() + " " + getDescriptor(); - } - - /** - * Copies all constant pool items to a given new constant pool - * and replaces the original items with the new ones. - * This is used for garbage collecting the items of removed fields - * and methods. - * - * @param cp the destination - */ - void compact(ConstPool cp) { - name = cp.addUtf8Info(getName()); - descriptor = cp.addUtf8Info(getDescriptor()); - attribute = AttributeInfo.copyAll(attribute, cp); - constPool = cp; - } - - void prune(ConstPool cp) { - ArrayList newAttributes = new ArrayList(); - - AttributeInfo invisibleAnnotations - = getAttribute(AnnotationsAttribute.invisibleTag); - if (invisibleAnnotations != null) { - invisibleAnnotations = invisibleAnnotations.copy(cp, null); - newAttributes.add(invisibleAnnotations); - } - - AttributeInfo visibleAnnotations - = getAttribute(AnnotationsAttribute.visibleTag); - if (visibleAnnotations != null) { - visibleAnnotations = visibleAnnotations.copy(cp, null); - newAttributes.add(visibleAnnotations); - } - - AttributeInfo parameterInvisibleAnnotations - = getAttribute(ParameterAnnotationsAttribute.invisibleTag); - if (parameterInvisibleAnnotations != null) { - parameterInvisibleAnnotations = parameterInvisibleAnnotations.copy(cp, null); - newAttributes.add(parameterInvisibleAnnotations); - } - - AttributeInfo parameterVisibleAnnotations - = getAttribute(ParameterAnnotationsAttribute.visibleTag); - if (parameterVisibleAnnotations != null) { - parameterVisibleAnnotations = parameterVisibleAnnotations.copy(cp, null); - newAttributes.add(parameterVisibleAnnotations); - } - - AnnotationDefaultAttribute defaultAttribute - = (AnnotationDefaultAttribute) getAttribute(AnnotationDefaultAttribute.tag); - if (defaultAttribute != null) - newAttributes.add(defaultAttribute); - - ExceptionsAttribute ea = getExceptionsAttribute(); - if (ea != null) - newAttributes.add(ea); - - AttributeInfo signature - = getAttribute(SignatureAttribute.tag); - if (signature != null) { - signature = signature.copy(cp, null); - newAttributes.add(signature); - } - - attribute = newAttributes; - name = cp.addUtf8Info(getName()); - descriptor = cp.addUtf8Info(getDescriptor()); - constPool = cp; - } - - /** - * Returns a method name. - */ - public String getName() { - if (cachedName == null) - cachedName = constPool.getUtf8Info(name); - - return cachedName; - } - - /** - * Sets a method name. - */ - public void setName(String newName) { - name = constPool.addUtf8Info(newName); - cachedName = newName; - } - - /** - * Returns true if this is not a constructor or a class initializer (static - * initializer). - */ - public boolean isMethod() { - String n = getName(); - return !n.equals(nameInit) && !n.equals(nameClinit); - } - - /** - * Returns a constant pool table used by this method. - */ - public ConstPool getConstPool() { - return constPool; - } - - /** - * Returns true if this is a constructor. - */ - public boolean isConstructor() { - return getName().equals(nameInit); - } - - /** - * Returns true if this is a class initializer (static initializer). - */ - public boolean isStaticInitializer() { - return getName().equals(nameClinit); - } - - /** - * Returns access flags. - * - * @see AccessFlag - */ - public int getAccessFlags() { - return accessFlags; - } - - /** - * Sets access flags. - * - * @see AccessFlag - */ - public void setAccessFlags(int acc) { - accessFlags = acc; - } - - /** - * Returns a method descriptor. - * - * @see Descriptor - */ - public String getDescriptor() { - return constPool.getUtf8Info(descriptor); - } - - /** - * Sets a method descriptor. - * - * @see Descriptor - */ - public void setDescriptor(String desc) { - if (!desc.equals(getDescriptor())) - descriptor = constPool.addUtf8Info(desc); - } - - /** - * Returns all the attributes. The returned List object - * is shared with this object. If you add a new attribute to the list, - * the attribute is also added to the method represented by this - * object. If you remove an attribute from the list, it is also removed - * from the method. - * - * @return a list of AttributeInfo objects. - * @see AttributeInfo - */ - public List getAttributes() { - if (attribute == null) - attribute = new ArrayList(); - - return attribute; - } - - /** - * Returns the attribute with the specified name. If it is not found, this - * method returns null. - * - * @param name attribute name - * @return an AttributeInfo object or null. - * @see #getAttributes() - */ - public AttributeInfo getAttribute(String name) { - return AttributeInfo.lookup(attribute, name); - } - - /** - * Appends an attribute. If there is already an attribute with the same - * name, the new one substitutes for it. - * - * @see #getAttributes() - */ - public void addAttribute(AttributeInfo info) { - if (attribute == null) - attribute = new ArrayList(); - - AttributeInfo.remove(attribute, info.getName()); - attribute.add(info); - } - - /** - * Returns an Exceptions attribute. - * - * @return an Exceptions attribute or null if it is not specified. - */ - public ExceptionsAttribute getExceptionsAttribute() { - AttributeInfo info = AttributeInfo.lookup(attribute, - ExceptionsAttribute.tag); - return (ExceptionsAttribute)info; - } - - /** - * Returns a Code attribute. - * - * @return a Code attribute or null if it is not specified. - */ - public CodeAttribute getCodeAttribute() { - AttributeInfo info = AttributeInfo.lookup(attribute, CodeAttribute.tag); - return (CodeAttribute)info; - } - - /** - * Removes an Exception attribute. - */ - public void removeExceptionsAttribute() { - AttributeInfo.remove(attribute, ExceptionsAttribute.tag); - } - - /** - * Adds an Exception attribute. - * - *

- * The added attribute must share the same constant pool table as this - * method_info structure. - */ - public void setExceptionsAttribute(ExceptionsAttribute cattr) { - removeExceptionsAttribute(); - if (attribute == null) - attribute = new ArrayList(); - - attribute.add(cattr); - } - - /** - * Removes a Code attribute. - */ - public void removeCodeAttribute() { - AttributeInfo.remove(attribute, CodeAttribute.tag); - } - - /** - * Adds a Code attribute. - * - *

- * The added attribute must share the same constant pool table as this - * method_info structure. - */ - public void setCodeAttribute(CodeAttribute cattr) { - removeCodeAttribute(); - if (attribute == null) - attribute = new ArrayList(); - - attribute.add(cattr); - } - - /** - * Rebuilds a stack map table if the class file is for Java 6 - * or later. Java 5 or older Java VMs do not recognize a stack - * map table. If doPreverify is true, this method - * also rebuilds a stack map for J2ME (CLDC). - * - * @param pool used for making type hierarchy. - * @param cf rebuild if this class file is for Java 6 or later. - * @see #rebuildStackMap(ClassPool) - * @see #rebuildStackMapForME(ClassPool) - * @see #doPreverify - * @since 3.6 - */ - public void rebuildStackMapIf6(ClassPool pool, ClassFile cf) - throws BadBytecode - { - if (cf.getMajorVersion() >= ClassFile.JAVA_6) - rebuildStackMap(pool); - - if (doPreverify) - rebuildStackMapForME(pool); - } - - /** - * Rebuilds a stack map table. If no stack map table is included, - * a new one is created. If this MethodInfo does not - * include a code attribute, nothing happens. - * - * @param pool used for making type hierarchy. - * @see StackMapTable - * @since 3.6 - */ - public void rebuildStackMap(ClassPool pool) throws BadBytecode { - CodeAttribute ca = getCodeAttribute(); - if (ca != null) { - StackMapTable smt = MapMaker.make(pool, this); - ca.setAttribute(smt); - } - } - - /** - * Rebuilds a stack map table for J2ME (CLDC). If no stack map table is included, - * a new one is created. If this MethodInfo does not - * include a code attribute, nothing happens. - * - * @param pool used for making type hierarchy. - * @see StackMap - * @since 3.12 - */ - public void rebuildStackMapForME(ClassPool pool) throws BadBytecode { - CodeAttribute ca = getCodeAttribute(); - if (ca != null) { - StackMap sm = MapMaker.make2(pool, this); - ca.setAttribute(sm); - } - } - - /** - * Returns the line number of the source line corresponding to the specified - * bytecode contained in this method. - * - * @param pos - * the position of the bytecode (>= 0). an index into the code - * array. - * @return -1 if this information is not available. - */ - public int getLineNumber(int pos) { - CodeAttribute ca = getCodeAttribute(); - if (ca == null) - return -1; - - LineNumberAttribute ainfo = (LineNumberAttribute)ca - .getAttribute(LineNumberAttribute.tag); - if (ainfo == null) - return -1; - - return ainfo.toLineNumber(pos); - } - - /** - * Changes a super constructor called by this constructor. - * - *

- * This method modifies a call to super(), which should be - * at the head of a constructor body, so that a constructor in a different - * super class is called. This method does not change actual parameters. - * Hence the new super class must have a constructor with the same signature - * as the original one. - * - *

- * This method should be called when the super class of the class declaring - * this method is changed. - * - *

- * This method does not perform anything unless this MethodInfo - * represents a constructor. - * - * @param superclass - * the new super class - */ - public void setSuperclass(String superclass) throws BadBytecode { - if (!isConstructor()) - return; - - CodeAttribute ca = getCodeAttribute(); - byte[] code = ca.getCode(); - CodeIterator iterator = ca.iterator(); - int pos = iterator.skipSuperConstructor(); - if (pos >= 0) { // not this() - ConstPool cp = constPool; - int mref = ByteArray.readU16bit(code, pos + 1); - int nt = cp.getMethodrefNameAndType(mref); - int sc = cp.addClassInfo(superclass); - int mref2 = cp.addMethodrefInfo(sc, nt); - ByteArray.write16bit(mref2, code, pos + 1); - } - } - - private void read(MethodInfo src, String methodname, Map classnames) - throws BadBytecode { - ConstPool destCp = constPool; - accessFlags = src.accessFlags; - name = destCp.addUtf8Info(methodname); - cachedName = methodname; - ConstPool srcCp = src.constPool; - String desc = srcCp.getUtf8Info(src.descriptor); - String desc2 = Descriptor.rename(desc, classnames); - descriptor = destCp.addUtf8Info(desc2); - - attribute = new ArrayList(); - ExceptionsAttribute eattr = src.getExceptionsAttribute(); - if (eattr != null) - attribute.add(eattr.copy(destCp, classnames)); - - CodeAttribute cattr = src.getCodeAttribute(); - if (cattr != null) - attribute.add(cattr.copy(destCp, classnames)); - } - - private void read(DataInputStream in) throws IOException { - accessFlags = in.readUnsignedShort(); - name = in.readUnsignedShort(); - descriptor = in.readUnsignedShort(); - int n = in.readUnsignedShort(); - attribute = new ArrayList(); - for (int i = 0; i < n; ++i) - attribute.add(AttributeInfo.read(constPool, in)); - } - - void write(DataOutputStream out) throws IOException { - out.writeShort(accessFlags); - out.writeShort(name); - out.writeShort(descriptor); - - if (attribute == null) - out.writeShort(0); - else { - out.writeShort(attribute.size()); - AttributeInfo.writeAll(attribute, out); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java deleted file mode 100644 index 67ed7f6..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -/** - * MethodParameters_attribute. - */ -public class MethodParametersAttribute extends AttributeInfo { - /** - * The name of this attribute "MethodParameters". - */ - public static final String tag = "MethodParameters"; - - MethodParametersAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Constructs an attribute. - * - * @param cp a constant pool table. - * @param names an array of parameter names. - * The i-th element is the name of the i-th parameter. - * @param flags an array of parameter access flags. - */ - public MethodParametersAttribute(ConstPool cp, String[] names, int[] flags) { - super(cp, tag); - byte[] data = new byte[names.length * 4 + 1]; - data[0] = (byte)names.length; - for (int i = 0; i < names.length; i++) { - ByteArray.write16bit(cp.addUtf8Info(names[i]), data, i * 4 + 1); - ByteArray.write16bit(flags[i], data, i * 4 + 3); - } - - set(data); - } - - /** - * Returns parameters_count, which is the number of - * parameters. - */ - public int size() { - return info[0] & 0xff; - } - - /** - * Returns the value of name_index of the i-th element of parameters. - * - * @param i the position of the parameter. - */ - public int name(int i) { - return ByteArray.readU16bit(info, i * 4 + 1); - } - - /** - * Returns the value of access_flags of the i-th element of parameters. - * - * @param i the position of the parameter. - * @see AccessFlag - */ - public int accessFlags(int i) { - return ByteArray.readU16bit(info, i * 4 + 3); - } - - /** - * Makes a copy. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames ignored. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - int s = size(); - ConstPool cp = getConstPool(); - String[] names = new String[s]; - int[] flags = new int[s]; - for (int i = 0; i < s; i++) { - names[i] = cp.getUtf8Info(name(i)); - flags[i] = accessFlags(i); - } - - return new MethodParametersAttribute(newCp, names, flags); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/Mnemonic.java b/src/com/wenshuo/agent/javassist/bytecode/Mnemonic.java deleted file mode 100644 index d5e465d..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/Mnemonic.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -/** - * JVM Instruction Names. - * - *

This interface has been separated from javassist.bytecode.Opcode - * because typical bytecode translators do not use mnemonics. If this - * interface were merged with Opcode, extra memory would be unnecessary - * consumed. - * - * @see Opcode - */ -public interface Mnemonic { - - /** - * The instruction names (mnemonics) sorted by the opcode. - * The length of this array is 202 (jsr_w=201). - */ - String[] OPCODE = { - "nop", /* 0*/ - "aconst_null", /* 1*/ - "iconst_m1", /* 2*/ - "iconst_0", /* 3*/ - "iconst_1", /* 4*/ - "iconst_2", /* 5*/ - "iconst_3", /* 6*/ - "iconst_4", /* 7*/ - "iconst_5", /* 8*/ - "lconst_0", /* 9*/ - "lconst_1", /* 10*/ - "fconst_0", /* 11*/ - "fconst_1", /* 12*/ - "fconst_2", /* 13*/ - "dconst_0", /* 14*/ - "dconst_1", /* 15*/ - "bipush", /* 16*/ - "sipush", /* 17*/ - "ldc", /* 18*/ - "ldc_w", /* 19*/ - "ldc2_w", /* 20*/ - "iload", /* 21*/ - "lload", /* 22*/ - "fload", /* 23*/ - "dload", /* 24*/ - "aload", /* 25*/ - "iload_0", /* 26*/ - "iload_1", /* 27*/ - "iload_2", /* 28*/ - "iload_3", /* 29*/ - "lload_0", /* 30*/ - "lload_1", /* 31*/ - "lload_2", /* 32*/ - "lload_3", /* 33*/ - "fload_0", /* 34*/ - "fload_1", /* 35*/ - "fload_2", /* 36*/ - "fload_3", /* 37*/ - "dload_0", /* 38*/ - "dload_1", /* 39*/ - "dload_2", /* 40*/ - "dload_3", /* 41*/ - "aload_0", /* 42*/ - "aload_1", /* 43*/ - "aload_2", /* 44*/ - "aload_3", /* 45*/ - "iaload", /* 46*/ - "laload", /* 47*/ - "faload", /* 48*/ - "daload", /* 49*/ - "aaload", /* 50*/ - "baload", /* 51*/ - "caload", /* 52*/ - "saload", /* 53*/ - "istore", /* 54*/ - "lstore", /* 55*/ - "fstore", /* 56*/ - "dstore", /* 57*/ - "astore", /* 58*/ - "istore_0", /* 59*/ - "istore_1", /* 60*/ - "istore_2", /* 61*/ - "istore_3", /* 62*/ - "lstore_0", /* 63*/ - "lstore_1", /* 64*/ - "lstore_2", /* 65*/ - "lstore_3", /* 66*/ - "fstore_0", /* 67*/ - "fstore_1", /* 68*/ - "fstore_2", /* 69*/ - "fstore_3", /* 70*/ - "dstore_0", /* 71*/ - "dstore_1", /* 72*/ - "dstore_2", /* 73*/ - "dstore_3", /* 74*/ - "astore_0", /* 75*/ - "astore_1", /* 76*/ - "astore_2", /* 77*/ - "astore_3", /* 78*/ - "iastore", /* 79*/ - "lastore", /* 80*/ - "fastore", /* 81*/ - "dastore", /* 82*/ - "aastore", /* 83*/ - "bastore", /* 84*/ - "castore", /* 85*/ - "sastore", /* 86*/ - "pop", /* 87*/ - "pop2", /* 88*/ - "dup", /* 89*/ - "dup_x1", /* 90*/ - "dup_x2", /* 91*/ - "dup2", /* 92*/ - "dup2_x1", /* 93*/ - "dup2_x2", /* 94*/ - "swap", /* 95*/ - "iadd", /* 96*/ - "ladd", /* 97*/ - "fadd", /* 98*/ - "dadd", /* 99*/ - "isub", /* 100*/ - "lsub", /* 101*/ - "fsub", /* 102*/ - "dsub", /* 103*/ - "imul", /* 104*/ - "lmul", /* 105*/ - "fmul", /* 106*/ - "dmul", /* 107*/ - "idiv", /* 108*/ - "ldiv", /* 109*/ - "fdiv", /* 110*/ - "ddiv", /* 111*/ - "irem", /* 112*/ - "lrem", /* 113*/ - "frem", /* 114*/ - "drem", /* 115*/ - "ineg", /* 116*/ - "lneg", /* 117*/ - "fneg", /* 118*/ - "dneg", /* 119*/ - "ishl", /* 120*/ - "lshl", /* 121*/ - "ishr", /* 122*/ - "lshr", /* 123*/ - "iushr", /* 124*/ - "lushr", /* 125*/ - "iand", /* 126*/ - "land", /* 127*/ - "ior", /* 128*/ - "lor", /* 129*/ - "ixor", /* 130*/ - "lxor", /* 131*/ - "iinc", /* 132*/ - "i2l", /* 133*/ - "i2f", /* 134*/ - "i2d", /* 135*/ - "l2i", /* 136*/ - "l2f", /* 137*/ - "l2d", /* 138*/ - "f2i", /* 139*/ - "f2l", /* 140*/ - "f2d", /* 141*/ - "d2i", /* 142*/ - "d2l", /* 143*/ - "d2f", /* 144*/ - "i2b", /* 145*/ - "i2c", /* 146*/ - "i2s", /* 147*/ - "lcmp", /* 148*/ - "fcmpl", /* 149*/ - "fcmpg", /* 150*/ - "dcmpl", /* 151*/ - "dcmpg", /* 152*/ - "ifeq", /* 153*/ - "ifne", /* 154*/ - "iflt", /* 155*/ - "ifge", /* 156*/ - "ifgt", /* 157*/ - "ifle", /* 158*/ - "if_icmpeq", /* 159*/ - "if_icmpne", /* 160*/ - "if_icmplt", /* 161*/ - "if_icmpge", /* 162*/ - "if_icmpgt", /* 163*/ - "if_icmple", /* 164*/ - "if_acmpeq", /* 165*/ - "if_acmpne", /* 166*/ - "goto", /* 167*/ - "jsr", /* 168*/ - "ret", /* 169*/ - "tableswitch", /* 170*/ - "lookupswitch", /* 171*/ - "ireturn", /* 172*/ - "lreturn", /* 173*/ - "freturn", /* 174*/ - "dreturn", /* 175*/ - "areturn", /* 176*/ - "return", /* 177*/ - "getstatic", /* 178*/ - "putstatic", /* 179*/ - "getfield", /* 180*/ - "putfield", /* 181*/ - "invokevirtual", /* 182*/ - "invokespecial", /* 183*/ - "invokestatic", /* 184*/ - "invokeinterface", /* 185*/ - "invokedynamic", /* 186 */ - "new", /* 187*/ - "newarray", /* 188*/ - "anewarray", /* 189*/ - "arraylength", /* 190*/ - "athrow", /* 191*/ - "checkcast", /* 192*/ - "instanceof", /* 193*/ - "monitorenter", /* 194*/ - "monitorexit", /* 195*/ - "wide", /* 196*/ - "multianewarray", /* 197*/ - "ifnull", /* 198*/ - "ifnonnull", /* 199*/ - "goto_w", /* 200*/ - "jsr_w" /* 201*/ - }; -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/Opcode.java b/src/com/wenshuo/agent/javassist/bytecode/Opcode.java deleted file mode 100644 index 0a8ea3a..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/Opcode.java +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -/** - * JVM Instruction Set. - * - *

This interface defines opcodes and - * array types for the NEWARRAY instruction. - * - * @see Mnemonic - */ -public interface Opcode { - /* Opcodes */ - - int AALOAD = 50; - int AASTORE = 83; - int ACONST_NULL = 1; - int ALOAD = 25; - int ALOAD_0 = 42; - int ALOAD_1 = 43; - int ALOAD_2 = 44; - int ALOAD_3 = 45; - int ANEWARRAY = 189; - int ARETURN = 176; - int ARRAYLENGTH = 190; - int ASTORE = 58; - int ASTORE_0 = 75; - int ASTORE_1 = 76; - int ASTORE_2 = 77; - int ASTORE_3 = 78; - int ATHROW = 191; - int BALOAD = 51; - int BASTORE = 84; - int BIPUSH = 16; - int CALOAD = 52; - int CASTORE = 85; - int CHECKCAST = 192; - int D2F = 144; - int D2I = 142; - int D2L = 143; - int DADD = 99; - int DALOAD = 49; - int DASTORE = 82; - int DCMPG = 152; - int DCMPL = 151; - int DCONST_0 = 14; - int DCONST_1 = 15; - int DDIV = 111; - int DLOAD = 24; - int DLOAD_0 = 38; - int DLOAD_1 = 39; - int DLOAD_2 = 40; - int DLOAD_3 = 41; - int DMUL = 107; - int DNEG = 119; - int DREM = 115; - int DRETURN = 175; - int DSTORE = 57; - int DSTORE_0 = 71; - int DSTORE_1 = 72; - int DSTORE_2 = 73; - int DSTORE_3 = 74; - int DSUB = 103; - int DUP = 89; - int DUP2 = 92; - int DUP2_X1 = 93; - int DUP2_X2 = 94; - int DUP_X1 = 90; - int DUP_X2 = 91; - int F2D = 141; - int F2I = 139; - int F2L = 140; - int FADD = 98; - int FALOAD = 48; - int FASTORE = 81; - int FCMPG = 150; - int FCMPL = 149; - int FCONST_0 = 11; - int FCONST_1 = 12; - int FCONST_2 = 13; - int FDIV = 110; - int FLOAD = 23; - int FLOAD_0 = 34; - int FLOAD_1 = 35; - int FLOAD_2 = 36; - int FLOAD_3 = 37; - int FMUL = 106; - int FNEG = 118; - int FREM = 114; - int FRETURN = 174; - int FSTORE = 56; - int FSTORE_0 = 67; - int FSTORE_1 = 68; - int FSTORE_2 = 69; - int FSTORE_3 = 70; - int FSUB = 102; - int GETFIELD = 180; - int GETSTATIC = 178; - int GOTO = 167; - int GOTO_W = 200; - int I2B = 145; - int I2C = 146; - int I2D = 135; - int I2F = 134; - int I2L = 133; - int I2S = 147; - int IADD = 96; - int IALOAD = 46; - int IAND = 126; - int IASTORE = 79; - 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 ICONST_M1 = 2; - int IDIV = 108; - int IFEQ = 153; - int IFGE = 156; - int IFGT = 157; - int IFLE = 158; - int IFLT = 155; - int IFNE = 154; - int IFNONNULL = 199; - int IFNULL = 198; - int IF_ACMPEQ = 165; - int IF_ACMPNE = 166; - int IF_ICMPEQ = 159; - int IF_ICMPGE = 162; - int IF_ICMPGT = 163; - int IF_ICMPLE = 164; - int IF_ICMPLT = 161; - int IF_ICMPNE = 160; - int IINC = 132; - int ILOAD = 21; - int ILOAD_0 = 26; - int ILOAD_1 = 27; - int ILOAD_2 = 28; - int ILOAD_3 = 29; - int IMUL = 104; - int INEG = 116; - int INSTANCEOF = 193; - int INVOKEDYNAMIC = 186; - int INVOKEINTERFACE = 185; - int INVOKESPECIAL = 183; - int INVOKESTATIC = 184; - int INVOKEVIRTUAL = 182; - int IOR = 128; - int IREM = 112; - int IRETURN = 172; - int ISHL = 120; - int ISHR = 122; - int ISTORE = 54; - int ISTORE_0 = 59; - int ISTORE_1 = 60; - int ISTORE_2 = 61; - int ISTORE_3 = 62; - int ISUB = 100; - int IUSHR = 124; - int IXOR = 130; - int JSR = 168; - int JSR_W = 201; - int L2D = 138; - int L2F = 137; - int L2I = 136; - int LADD = 97; - int LALOAD = 47; - int LAND = 127; - int LASTORE = 80; - int LCMP = 148; - int LCONST_0 = 9; - int LCONST_1 = 10; - int LDC = 18; - int LDC2_W = 20; - int LDC_W = 19; - int LDIV = 109; - int LLOAD = 22; - int LLOAD_0 = 30; - int LLOAD_1 = 31; - int LLOAD_2 = 32; - int LLOAD_3 = 33; - int LMUL = 105; - int LNEG = 117; - int LOOKUPSWITCH = 171; - int LOR = 129; - int LREM = 113; - int LRETURN = 173; - int LSHL = 121; - int LSHR = 123; - int LSTORE = 55; - int LSTORE_0 = 63; - int LSTORE_1 = 64; - int LSTORE_2 = 65; - int LSTORE_3 = 66; - int LSUB = 101; - int LUSHR = 125; - int LXOR = 131; - int MONITORENTER = 194; - int MONITOREXIT = 195; - int MULTIANEWARRAY = 197; - int NEW = 187; - int NEWARRAY = 188; - int NOP = 0; - int POP = 87; - int POP2 = 88; - int PUTFIELD = 181; - int PUTSTATIC = 179; - int RET = 169; - int RETURN = 177; - int SALOAD = 53; - int SASTORE = 86; - int SIPUSH = 17; - int SWAP = 95; - int TABLESWITCH = 170; - int WIDE = 196; - - /* array-type code for the newarray instruction */ - - 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; - - /* how many values are pushed on the operand stack. */ - int[] STACK_GROW = { - 0, // nop, 0 - 1, // aconst_null, 1 - 1, // iconst_m1, 2 - 1, // iconst_0, 3 - 1, // iconst_1, 4 - 1, // iconst_2, 5 - 1, // iconst_3, 6 - 1, // iconst_4, 7 - 1, // iconst_5, 8 - 2, // lconst_0, 9 - 2, // lconst_1, 10 - 1, // fconst_0, 11 - 1, // fconst_1, 12 - 1, // fconst_2, 13 - 2, // dconst_0, 14 - 2, // dconst_1, 15 - 1, // bipush, 16 - 1, // sipush, 17 - 1, // ldc, 18 - 1, // ldc_w, 19 - 2, // ldc2_w, 20 - 1, // iload, 21 - 2, // lload, 22 - 1, // fload, 23 - 2, // dload, 24 - 1, // aload, 25 - 1, // iload_0, 26 - 1, // iload_1, 27 - 1, // iload_2, 28 - 1, // iload_3, 29 - 2, // lload_0, 30 - 2, // lload_1, 31 - 2, // lload_2, 32 - 2, // lload_3, 33 - 1, // fload_0, 34 - 1, // fload_1, 35 - 1, // fload_2, 36 - 1, // fload_3, 37 - 2, // dload_0, 38 - 2, // dload_1, 39 - 2, // dload_2, 40 - 2, // dload_3, 41 - 1, // aload_0, 42 - 1, // aload_1, 43 - 1, // aload_2, 44 - 1, // aload_3, 45 - -1, // iaload, 46 - 0, // laload, 47 - -1, // faload, 48 - 0, // daload, 49 - -1, // aaload, 50 - -1, // baload, 51 - -1, // caload, 52 - -1, // saload, 53 - -1, // istore, 54 - -2, // lstore, 55 - -1, // fstore, 56 - -2, // dstore, 57 - -1, // astore, 58 - -1, // istore_0, 59 - -1, // istore_1, 60 - -1, // istore_2, 61 - -1, // istore_3, 62 - -2, // lstore_0, 63 - -2, // lstore_1, 64 - -2, // lstore_2, 65 - -2, // lstore_3, 66 - -1, // fstore_0, 67 - -1, // fstore_1, 68 - -1, // fstore_2, 69 - -1, // fstore_3, 70 - -2, // dstore_0, 71 - -2, // dstore_1, 72 - -2, // dstore_2, 73 - -2, // dstore_3, 74 - -1, // astore_0, 75 - -1, // astore_1, 76 - -1, // astore_2, 77 - -1, // astore_3, 78 - -3, // iastore, 79 - -4, // lastore, 80 - -3, // fastore, 81 - -4, // dastore, 82 - -3, // aastore, 83 - -3, // bastore, 84 - -3, // castore, 85 - -3, // sastore, 86 - -1, // pop, 87 - -2, // pop2, 88 - 1, // dup, 89 - 1, // dup_x1, 90 - 1, // dup_x2, 91 - 2, // dup2, 92 - 2, // dup2_x1, 93 - 2, // dup2_x2, 94 - 0, // swap, 95 - -1, // iadd, 96 - -2, // ladd, 97 - -1, // fadd, 98 - -2, // dadd, 99 - -1, // isub, 100 - -2, // lsub, 101 - -1, // fsub, 102 - -2, // dsub, 103 - -1, // imul, 104 - -2, // lmul, 105 - -1, // fmul, 106 - -2, // dmul, 107 - -1, // idiv, 108 - -2, // ldiv, 109 - -1, // fdiv, 110 - -2, // ddiv, 111 - -1, // irem, 112 - -2, // lrem, 113 - -1, // frem, 114 - -2, // drem, 115 - 0, // ineg, 116 - 0, // lneg, 117 - 0, // fneg, 118 - 0, // dneg, 119 - -1, // ishl, 120 - -1, // lshl, 121 - -1, // ishr, 122 - -1, // lshr, 123 - -1, // iushr, 124 - -1, // lushr, 125 - -1, // iand, 126 - -2, // land, 127 - -1, // ior, 128 - -2, // lor, 129 - -1, // ixor, 130 - -2, // lxor, 131 - 0, // iinc, 132 - 1, // i2l, 133 - 0, // i2f, 134 - 1, // i2d, 135 - -1, // l2i, 136 - -1, // l2f, 137 - 0, // l2d, 138 - 0, // f2i, 139 - 1, // f2l, 140 - 1, // f2d, 141 - -1, // d2i, 142 - 0, // d2l, 143 - -1, // d2f, 144 - 0, // i2b, 145 - 0, // i2c, 146 - 0, // i2s, 147 - -3, // lcmp, 148 - -1, // fcmpl, 149 - -1, // fcmpg, 150 - -3, // dcmpl, 151 - -3, // dcmpg, 152 - -1, // ifeq, 153 - -1, // ifne, 154 - -1, // iflt, 155 - -1, // ifge, 156 - -1, // ifgt, 157 - -1, // ifle, 158 - -2, // if_icmpeq, 159 - -2, // if_icmpne, 160 - -2, // if_icmplt, 161 - -2, // if_icmpge, 162 - -2, // if_icmpgt, 163 - -2, // if_icmple, 164 - -2, // if_acmpeq, 165 - -2, // if_acmpne, 166 - 0, // goto, 167 - 1, // jsr, 168 - 0, // ret, 169 - -1, // tableswitch, 170 - -1, // lookupswitch, 171 - -1, // ireturn, 172 - -2, // lreturn, 173 - -1, // freturn, 174 - -2, // dreturn, 175 - -1, // areturn, 176 - 0, // return, 177 - 0, // getstatic, 178 depends on the type - 0, // putstatic, 179 depends on the type - 0, // getfield, 180 depends on the type - 0, // putfield, 181 depends on the type - 0, // invokevirtual, 182 depends on the type - 0, // invokespecial, 183 depends on the type - 0, // invokestatic, 184 depends on the type - 0, // invokeinterface, 185 depends on the type - 0, // invokedynaimc, 186 depends on the type - 1, // new, 187 - 0, // newarray, 188 - 0, // anewarray, 189 - 0, // arraylength, 190 - -1, // athrow, 191 stack is cleared - 0, // checkcast, 192 - 0, // instanceof, 193 - -1, // monitorenter, 194 - -1, // monitorexit, 195 - 0, // wide, 196 depends on the following opcode - 0, // multianewarray, 197 depends on the dimensions - -1, // ifnull, 198 - -1, // ifnonnull, 199 - 0, // goto_w, 200 - 1 // jsr_w, 201 - }; -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java deleted file mode 100644 index cdefada..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.util.HashMap; -import java.util.Map; -import java.io.IOException; -import java.io.DataInputStream; -import java.io.ByteArrayOutputStream; - -import com.wenshuo.agent.javassist.bytecode.AnnotationsAttribute.Copier; -import com.wenshuo.agent.javassist.bytecode.AnnotationsAttribute.Parser; -import com.wenshuo.agent.javassist.bytecode.AnnotationsAttribute.Renamer; -import com.wenshuo.agent.javassist.bytecode.annotation.*; - -/** - * A class representing RuntimeVisibleAnnotations_attribute and - * RuntimeInvisibleAnnotations_attribute. - * - *

To obtain an ParameterAnnotationAttribute object, invoke - * getAttribute(ParameterAnnotationsAttribute.invisibleTag) - * in MethodInfo. - * The obtained attribute is a - * runtime invisible annotations attribute. - * If the parameter is - * ParameterAnnotationAttribute.visibleTag, then the obtained - * attribute is a runtime visible one. - */ -public class ParameterAnnotationsAttribute extends AttributeInfo { - /** - * The name of the RuntimeVisibleParameterAnnotations - * attribute. - */ - public static final String visibleTag - = "RuntimeVisibleParameterAnnotations"; - - /** - * The name of the RuntimeInvisibleParameterAnnotations - * attribute. - */ - public static final String invisibleTag - = "RuntimeInvisibleParameterAnnotations"; - /** - * Constructs - * a Runtime(In)VisibleParameterAnnotations_attribute. - * - * @param cp constant pool - * @param attrname attribute name (visibleTag or - * invisibleTag). - * @param info the contents of this attribute. It does not - * include attribute_name_index or - * attribute_length. - */ - public ParameterAnnotationsAttribute(ConstPool cp, String attrname, - byte[] info) { - super(cp, attrname, info); - } - - /** - * Constructs an empty - * Runtime(In)VisibleParameterAnnotations_attribute. - * A new annotation can be later added to the created attribute - * by setAnnotations(). - * - * @param cp constant pool - * @param attrname attribute name (visibleTag or - * invisibleTag). - * @see #setAnnotations(Annotation[][]) - */ - public ParameterAnnotationsAttribute(ConstPool cp, String attrname) { - this(cp, attrname, new byte[] { 0 }); - } - - /** - * @param n the attribute name. - */ - ParameterAnnotationsAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Returns num_parameters. - */ - public int numParameters() { - return info[0] & 0xff; - } - - /** - * Copies this attribute and returns a new copy. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - Copier copier = new Copier(info, constPool, newCp, classnames); - try { - copier.parameters(); - return new ParameterAnnotationsAttribute(newCp, getName(), - copier.close()); - } - catch (Exception e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Parses the annotations and returns a data structure representing - * that parsed annotations. Note that changes of the node values of the - * returned tree are not reflected on the annotations represented by - * this object unless the tree is copied back to this object by - * setAnnotations(). - * - * @return Each element of the returned array represents an array of - * annotations that are associated with each method parameter. - * - * @see #setAnnotations(Annotation[][]) - */ - public Annotation[][] getAnnotations() { - try { - return new Parser(info, constPool).parseParameters(); - } - catch (Exception e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Changes the annotations represented by this object according to - * the given array of Annotation objects. - * - * @param params the data structure representing the - * new annotations. Every element of this array - * is an array of Annotation and - * it represens annotations of each method parameter. - */ - public void setAnnotations(Annotation[][] params) { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - AnnotationsWriter writer = new AnnotationsWriter(output, constPool); - try { - int n = params.length; - writer.numParameters(n); - for (int i = 0; i < n; ++i) { - Annotation[] anno = params[i]; - writer.numAnnotations(anno.length); - for (int j = 0; j < anno.length; ++j) - anno[j].write(writer); - } - - writer.close(); - } - catch (IOException e) { - throw new RuntimeException(e); // should never reach here. - } - - set(output.toByteArray()); - } - - /** - * @param oldname a JVM class name. - * @param newname a JVM class name. - */ - void renameClass(String oldname, String newname) { - HashMap map = new HashMap(); - map.put(oldname, newname); - renameClass(map); - } - - void renameClass(Map classnames) { - Renamer renamer = new Renamer(info, getConstPool(), classnames); - try { - renamer.parameters(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - void getRefClasses(Map classnames) { renameClass(classnames); } - - /** - * Returns a string representation of this object. - */ - public String toString() { - Annotation[][] aa = getAnnotations(); - StringBuilder sbuf = new StringBuilder(); - int k = 0; - while (k < aa.length) { - Annotation[] a = aa[k++]; - int i = 0; - while (i < a.length) { - sbuf.append(a[i++].toString()); - if (i != a.length) - sbuf.append(" "); - } - - if (k != aa.length) - sbuf.append(", "); - } - - return sbuf.toString(); - - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java deleted file mode 100644 index f560505..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java +++ /dev/null @@ -1,1160 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; -import java.util.ArrayList; -import com.wenshuo.agent.javassist.CtClass; - -/** - * Signature_attribute. - */ -public class SignatureAttribute extends AttributeInfo { - /** - * The name of this attribute "Signature". - */ - public static final String tag = "Signature"; - - SignatureAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Constructs a Signature attribute. - * - * @param cp a constant pool table. - * @param signature the signature represented by this attribute. - */ - public SignatureAttribute(ConstPool cp, String signature) { - super(cp, tag); - int index = cp.addUtf8Info(signature); - byte[] bvalue = new byte[2]; - bvalue[0] = (byte)(index >>> 8); - bvalue[1] = (byte)index; - set(bvalue); - } - - /** - * Returns the generic signature indicated by signature_index. - * - * @see #toClassSignature(String) - * @see #toMethodSignature(String) - * @see #toFieldSignature(String) - */ - public String getSignature() { - return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0)); - } - - /** - * Sets signature_index to the index of the given generic signature, - * which is added to a constant pool. - * - * @param sig new signature. - * @since 3.11 - */ - public void setSignature(String sig) { - int index = getConstPool().addUtf8Info(sig); - ByteArray.write16bit(index, info, 0); - } - - /** - * Makes a copy. Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - return new SignatureAttribute(newCp, getSignature()); - } - - void renameClass(String oldname, String newname) { - String sig = renameClass(getSignature(), oldname, newname); - setSignature(sig); - } - - void renameClass(Map classnames) { - String sig = renameClass(getSignature(), classnames); - setSignature(sig); - } - - static String renameClass(String desc, String oldname, String newname) { - Map map = new java.util.HashMap(); - map.put(oldname, newname); - return renameClass(desc, map); - } - - static String renameClass(String desc, Map map) { - if (map == null) - return desc; - - StringBuilder newdesc = new StringBuilder(); - int head = 0; - int i = 0; - for (;;) { - int j = desc.indexOf('L', i); - if (j < 0) - break; - - StringBuilder nameBuf = new StringBuilder(); - int k = j; - char c; - try { - while ((c = desc.charAt(++k)) != ';') { - nameBuf.append(c); - if (c == '<') { - while ((c = desc.charAt(++k)) != '>') - nameBuf.append(c); - - nameBuf.append(c); - } - } - } - catch (IndexOutOfBoundsException e) { break; } - i = k + 1; - String name = nameBuf.toString(); - String name2 = (String)map.get(name); - if (name2 != null) { - newdesc.append(desc.substring(head, j)); - newdesc.append('L'); - newdesc.append(name2); - newdesc.append(c); - head = i; - } - } - - if (head == 0) - return desc; - else { - int len = desc.length(); - if (head < len) - newdesc.append(desc.substring(head, len)); - - return newdesc.toString(); - } - } - - private static boolean isNamePart(int c) { - return c != ';' && c != '<'; - } - - static private class Cursor { - int position = 0; - - int indexOf(String s, int ch) throws BadBytecode { - int i = s.indexOf(ch, position); - if (i < 0) - throw error(s); - else { - position = i + 1; - return i; - } - } - } - - /** - * Class signature. - */ - public static class ClassSignature { - TypeParameter[] params; - ClassType superClass; - ClassType[] interfaces; - - /** - * Constructs a class signature. - * - * @param params type parameters. - * @param superClass the super class. - * @param interfaces the interface types. - */ - public ClassSignature(TypeParameter[] params, ClassType superClass, ClassType[] interfaces) { - this.params = params == null ? new TypeParameter[0] : params; - this.superClass = superClass == null ? ClassType.OBJECT : superClass; - this.interfaces = interfaces == null ? new ClassType[0] : interfaces; - } - - /** - * Constructs a class signature. - * - * @param p type parameters. - */ - public ClassSignature(TypeParameter[] p) { - this(p, null, null); - } - - /** - * Returns the type parameters. - * - * @return a zero-length array if the type parameters are not specified. - */ - public TypeParameter[] getParameters() { - return params; - } - - /** - * Returns the super class. - */ - public ClassType getSuperClass() { return superClass; } - - /** - * Returns the super interfaces. - * - * @return a zero-length array if the super interfaces are not specified. - */ - public ClassType[] getInterfaces() { return interfaces; } - - /** - * Returns the string representation. - */ - public String toString() { - StringBuffer sbuf = new StringBuffer(); - - TypeParameter.toString(sbuf, params); - sbuf.append(" extends ").append(superClass); - if (interfaces.length > 0) { - sbuf.append(" implements "); - Type.toString(sbuf, interfaces); - } - - return sbuf.toString(); - } - - /** - * Returns the encoded string representing the method type signature. - */ - public String encode() { - StringBuffer sbuf = new StringBuffer(); - if (params.length > 0) { - sbuf.append('<'); - for (int i = 0; i < params.length; i++) - params[i].encode(sbuf); - - sbuf.append('>'); - } - - superClass.encode(sbuf); - for (int i = 0; i < interfaces.length; i++) - interfaces[i].encode(sbuf); - - return sbuf.toString(); - } - } - - /** - * Method type signature. - */ - public static class MethodSignature { - TypeParameter[] typeParams; - Type[] params; - Type retType; - ObjectType[] exceptions; - - /** - * Constructs a method type signature. Any parameter can be null - * to represent void or nothing. - * - * @param tp type parameters. - * @param params parameter types. - * @param ret a return type, or null if the return type is void. - * @param ex exception types. - */ - public MethodSignature(TypeParameter[] tp, Type[] params, Type ret, ObjectType[] ex) { - typeParams = tp == null ? new TypeParameter[0] : tp; - this.params = params == null ? new Type[0] : params; - retType = ret == null ? new BaseType("void") : ret; - exceptions = ex == null ? new ObjectType[0] : ex; - } - - /** - * Returns the formal type parameters. - * - * @return a zero-length array if the type parameters are not specified. - */ - public TypeParameter[] getTypeParameters() { return typeParams; } - - /** - * Returns the types of the formal parameters. - * - * @return a zero-length array if no formal parameter is taken. - */ - public Type[] getParameterTypes() { return params; } - - /** - * Returns the type of the returned value. - */ - public Type getReturnType() { return retType; } - - /** - * Returns the types of the exceptions that may be thrown. - * - * @return a zero-length array if exceptions are never thrown or - * the exception types are not parameterized types or type variables. - */ - public ObjectType[] getExceptionTypes() { return exceptions; } - - /** - * Returns the string representation. - */ - public String toString() { - StringBuffer sbuf = new StringBuffer(); - - TypeParameter.toString(sbuf, typeParams); - sbuf.append(" ("); - Type.toString(sbuf, params); - sbuf.append(") "); - sbuf.append(retType); - if (exceptions.length > 0) { - sbuf.append(" throws "); - Type.toString(sbuf, exceptions); - } - - return sbuf.toString(); - } - - /** - * Returns the encoded string representing the method type signature. - */ - public String encode() { - StringBuffer sbuf = new StringBuffer(); - if (typeParams.length > 0) { - sbuf.append('<'); - for (int i = 0; i < typeParams.length; i++) - typeParams[i].encode(sbuf); - - sbuf.append('>'); - } - - sbuf.append('('); - for (int i = 0; i < params.length; i++) - params[i].encode(sbuf); - - sbuf.append(')'); - retType.encode(sbuf); - if (exceptions.length > 0) - for (int i = 0; i < exceptions.length; i++) { - sbuf.append('^'); - exceptions[i].encode(sbuf); - } - - return sbuf.toString(); - } - } - - /** - * Formal type parameters. - * - * @see TypeArgument - */ - public static class TypeParameter { - String name; - ObjectType superClass; - ObjectType[] superInterfaces; - - TypeParameter(String sig, int nb, int ne, ObjectType sc, ObjectType[] si) { - name = sig.substring(nb, ne); - superClass = sc; - superInterfaces = si; - } - - /** - * Constructs a TypeParameter representing a type parametre - * like <T extends ... >. - * - * @param name parameter name. - * @param superClass an upper bound class-type (or null). - * @param superInterfaces an upper bound interface-type (or null). - */ - public TypeParameter(String name, ObjectType superClass, ObjectType[] superInterfaces) { - this.name = name; - this.superClass = superClass; - if (superInterfaces == null) - this.superInterfaces = new ObjectType[0]; - else - this.superInterfaces = superInterfaces; - } - - /** - * Constructs a TypeParameter representing a type parameter - * like <T>. - * - * @param name parameter name. - */ - public TypeParameter(String name) { - this(name, null, null); - } - - /** - * Returns the name of the type parameter. - */ - public String getName() { - return name; - } - - /** - * Returns the class bound of this parameter. - */ - public ObjectType getClassBound() { return superClass; } - - /** - * Returns the interface bound of this parameter. - * - * @return a zero-length array if the interface bound is not specified. - */ - public ObjectType[] getInterfaceBound() { return superInterfaces; } - - /** - * Returns the string representation. - */ - public String toString() { - StringBuffer sbuf = new StringBuffer(getName()); - if (superClass != null) - sbuf.append(" extends ").append(superClass.toString()); - - int len = superInterfaces.length; - if (len > 0) { - for (int i = 0; i < len; i++) { - if (i > 0 || superClass != null) - sbuf.append(" & "); - else - sbuf.append(" extends "); - - sbuf.append(superInterfaces[i].toString()); - } - } - - return sbuf.toString(); - } - - static void toString(StringBuffer sbuf, TypeParameter[] tp) { - sbuf.append('<'); - for (int i = 0; i < tp.length; i++) { - if (i > 0) - sbuf.append(", "); - - sbuf.append(tp[i]); - } - - sbuf.append('>'); - } - - void encode(StringBuffer sb) { - sb.append(name); - if (superClass == null) - sb.append(":Ljava/lang/Object;"); - else { - sb.append(':'); - superClass.encode(sb); - } - - for (int i = 0; i < superInterfaces.length; i++) { - sb.append(':'); - superInterfaces[i].encode(sb); - } - } - } - - /** - * Type argument. - * - * @see TypeParameter - */ - public static class TypeArgument { - ObjectType arg; - char wildcard; - - TypeArgument(ObjectType a, char w) { - arg = a; - wildcard = w; - } - - /** - * Constructs a TypeArgument. - * A type argument is <String>, <int[]>, - * or a type variable <T>, etc. - * - * @param t a class type, an array type, or a type variable. - */ - public TypeArgument(ObjectType t) { - this(t, ' '); - } - - /** - * Constructs a TypeArgument representing <?>. - */ - public TypeArgument() { - this(null, '*'); - } - - /** - * A factory method constructing a TypeArgument with an upper bound. - * It represents <? extends ... > - * - * @param t an upper bound type. - */ - public static TypeArgument subclassOf(ObjectType t) { - return new TypeArgument(t, '+'); - } - - /** - * A factory method constructing a TypeArgument with an lower bound. - * It represents <? super ... > - * - * @param t an lower bbound type. - */ - public static TypeArgument superOf(ObjectType t) { - return new TypeArgument(t, '-'); - } - - /** - * Returns the kind of this type argument. - * - * @return ' ' (not-wildcard), '*' (wildcard), '+' (wildcard with - * upper bound), or '-' (wildcard with lower bound). - */ - public char getKind() { return wildcard; } - - /** - * Returns true if this type argument is a wildcard type - * such as ?, ? extends String, or ? super Integer. - */ - public boolean isWildcard() { return wildcard != ' '; } - - /** - * Returns the type represented by this argument - * if the argument is not a wildcard type. Otherwise, this method - * returns the upper bound (if the kind is '+'), - * the lower bound (if the kind is '-'), or null (if the upper or lower - * bound is not specified). - */ - public ObjectType getType() { return arg; } - - /** - * Returns the string representation. - */ - public String toString() { - if (wildcard == '*') - return "?"; - - String type = arg.toString(); - if (wildcard == ' ') - return type; - else if (wildcard == '+') - return "? extends " + type; - else - return "? super " + type; - } - - static void encode(StringBuffer sb, TypeArgument[] args) { - sb.append('<'); - for (int i = 0; i < args.length; i++) { - TypeArgument ta = args[i]; - if (ta.isWildcard()) - sb.append(ta.wildcard); - - if (ta.getType() != null) - ta.getType().encode(sb); - } - - sb.append('>'); - } - } - - /** - * Primitive types and object types. - */ - public static abstract class Type { - abstract void encode(StringBuffer sb); - static void toString(StringBuffer sbuf, Type[] ts) { - for (int i = 0; i < ts.length; i++) { - if (i > 0) - sbuf.append(", "); - - sbuf.append(ts[i]); - } - } - - /** - * Returns the type name in the JVM internal style. - * For example, if the type is a nested class {@code foo.Bar.Baz}, - * then {@code foo.Bar$Baz} is returned. - */ - public String jvmTypeName() { return toString(); } - } - - /** - * Primitive types. - */ - public static class BaseType extends Type { - char descriptor; - BaseType(char c) { descriptor = c; } - - /** - * Constructs a BaseType. - * - * @param typeName void, int, ... - */ - public BaseType(String typeName) { - this(Descriptor.of(typeName).charAt(0)); - } - - /** - * Returns the descriptor representing this primitive type. - * - * @see javassist.bytecode.Descriptor - */ - public char getDescriptor() { return descriptor; } - - /** - * Returns the CtClass representing this - * primitive type. - */ - public CtClass getCtlass() { - return Descriptor.toPrimitiveClass(descriptor); - } - - /** - * Returns the string representation. - */ - public String toString() { - return Descriptor.toClassName(Character.toString(descriptor)); - } - - void encode(StringBuffer sb) { - sb.append(descriptor); - } - } - - /** - * Class types, array types, and type variables. - * This class is also used for representing a field type. - */ - public static abstract class ObjectType extends Type { - /** - * Returns the encoded string representing the object type signature. - */ - public String encode() { - StringBuffer sb = new StringBuffer(); - encode(sb); - return sb.toString(); - } - } - - /** - * Class types. - */ - public static class ClassType extends ObjectType { - String name; - TypeArgument[] arguments; - - static ClassType make(String s, int b, int e, - TypeArgument[] targs, ClassType parent) { - if (parent == null) - return new ClassType(s, b, e, targs); - else - return new NestedClassType(s, b, e, targs, parent); - } - - ClassType(String signature, int begin, int end, TypeArgument[] targs) { - name = signature.substring(begin, end).replace('/', '.'); - arguments = targs; - } - - /** - * A class type representing java.lang.Object. - */ - public static ClassType OBJECT = new ClassType("java.lang.Object", null); - - /** - * Constructs a ClassType. It represents - * the name of a non-nested class. - * - * @param className a fully qualified class name. - * @param args type arguments or null. - */ - public ClassType(String className, TypeArgument[] args) { - name = className; - arguments = args; - } - - /** - * Constructs a ClassType. It represents - * the name of a non-nested class. - * - * @param className a fully qualified class name. - */ - public ClassType(String className) { - this(className, null); - } - - /** - * Returns the class name. - */ - public String getName() { - return name; - } - - /** - * Returns the type arguments. - * - * @return null if no type arguments are given to this class. - */ - public TypeArgument[] getTypeArguments() { return arguments; } - - /** - * If this class is a member of another class, returns the - * class in which this class is declared. - * - * @return null if this class is not a member of another class. - */ - public ClassType getDeclaringClass() { return null; } - - /** - * Returns the string representation. - */ - public String toString() { - StringBuffer sbuf = new StringBuffer(); - ClassType parent = getDeclaringClass(); - if (parent != null) - sbuf.append(parent.toString()).append('.'); - - return toString2(sbuf); - } - - private String toString2(StringBuffer sbuf) { - sbuf.append(name); - if (arguments != null) { - sbuf.append('<'); - int n = arguments.length; - for (int i = 0; i < n; i++) { - if (i > 0) - sbuf.append(", "); - - sbuf.append(arguments[i].toString()); - } - - sbuf.append('>'); - } - - return sbuf.toString(); - } - - /** - * Returns the type name in the JVM internal style. - * For example, if the type is a nested class {@code foo.Bar.Baz}, - * then {@code foo.Bar$Baz} is returned. - */ - public String jvmTypeName() { - StringBuffer sbuf = new StringBuffer(); - ClassType parent = getDeclaringClass(); - if (parent != null) - sbuf.append(parent.jvmTypeName()).append('$'); - - return toString2(sbuf); - } - - void encode(StringBuffer sb) { - sb.append('L'); - encode2(sb); - sb.append(';'); - } - - void encode2(StringBuffer sb) { - ClassType parent = getDeclaringClass(); - if (parent != null) { - parent.encode2(sb); - sb.append('$'); - } - - sb.append(name.replace('.', '/')); - if (arguments != null) - TypeArgument.encode(sb, arguments); - } - } - - /** - * Nested class types. - */ - public static class NestedClassType extends ClassType { - ClassType parent; - NestedClassType(String s, int b, int e, - TypeArgument[] targs, ClassType p) { - super(s, b, e, targs); - parent = p; - } - - /** - * Constructs a NestedClassType. - * - * @param parent the class surrounding this class type. - * @param className a simple class name. It does not include - * a package name or a parent's class name. - * @param args type parameters or null. - */ - public NestedClassType(ClassType parent, String className, TypeArgument[] args) { - super(className, args); - this.parent = parent; - } - - /** - * Returns the class that declares this nested class. - * This nested class is a member of that declaring class. - */ - public ClassType getDeclaringClass() { return parent; } - } - - /** - * Array types. - */ - public static class ArrayType extends ObjectType { - int dim; - Type componentType; - - /** - * Constructs an ArrayType. - * - * @param d dimension. - * @param comp the component type. - */ - public ArrayType(int d, Type comp) { - dim = d; - componentType = comp; - } - - /** - * Returns the dimension of the array. - */ - public int getDimension() { return dim; } - - /** - * Returns the component type. - */ - public Type getComponentType() { - return componentType; - } - - /** - * Returns the string representation. - */ - public String toString() { - StringBuffer sbuf = new StringBuffer(componentType.toString()); - for (int i = 0; i < dim; i++) - sbuf.append("[]"); - - return sbuf.toString(); - } - - void encode(StringBuffer sb) { - for (int i = 0; i < dim; i++) - sb.append('['); - - componentType.encode(sb); - } - } - - /** - * Type variables. - */ - public static class TypeVariable extends ObjectType { - String name; - - TypeVariable(String sig, int begin, int end) { - name = sig.substring(begin, end); - } - - /** - * Constructs a TypeVariable. - * - * @param name the name of a type variable. - */ - public TypeVariable(String name) { - this.name = name; - } - - /** - * Returns the variable name. - */ - public String getName() { - return name; - } - - /** - * Returns the string representation. - */ - public String toString() { - return name; - } - - void encode(StringBuffer sb) { - sb.append('T').append(name).append(';'); - } - } - - /** - * Parses the given signature string as a class signature. - * - * @param sig the signature obtained from the SignatureAttribute - * of a ClassFile. - * @return a tree-like data structure representing a class signature. It provides - * convenient accessor methods. - * @throws BadBytecode thrown when a syntactical error is found. - * @see #getSignature() - * @since 3.5 - */ - public static ClassSignature toClassSignature(String sig) throws BadBytecode { - try { - return parseSig(sig); - } - catch (IndexOutOfBoundsException e) { - throw error(sig); - } - } - - /** - * Parses the given signature string as a method type signature. - * - * @param sig the signature obtained from the SignatureAttribute - * of a MethodInfo. - * @return @return a tree-like data structure representing a method signature. It provides - * convenient accessor methods. - * @throws BadBytecode thrown when a syntactical error is found. - * @see #getSignature() - * @since 3.5 - */ - public static MethodSignature toMethodSignature(String sig) throws BadBytecode { - try { - return parseMethodSig(sig); - } - catch (IndexOutOfBoundsException e) { - throw error(sig); - } - } - - /** - * Parses the given signature string as a field type signature. - * - * @param sig the signature string obtained from the SignatureAttribute - * of a FieldInfo. - * @return the field type signature. - * @throws BadBytecode thrown when a syntactical error is found. - * @see #getSignature() - * @since 3.5 - */ - public static ObjectType toFieldSignature(String sig) throws BadBytecode { - try { - return parseObjectType(sig, new Cursor(), false); - } - catch (IndexOutOfBoundsException e) { - throw error(sig); - } - } - - /** - * Parses the given signature string as a type signature. - * The type signature is either the field type signature or a base type - * descriptor including void type. - * - * @throws BadBytecode thrown when a syntactical error is found. - * @since 3.18 - */ - public static Type toTypeSignature(String sig) throws BadBytecode { - try { - return parseType(sig, new Cursor()); - } - catch (IndexOutOfBoundsException e) { - throw error(sig); - } - } - - private static ClassSignature parseSig(String sig) - throws BadBytecode, IndexOutOfBoundsException - { - Cursor cur = new Cursor(); - TypeParameter[] tp = parseTypeParams(sig, cur); - ClassType superClass = parseClassType(sig, cur); - int sigLen = sig.length(); - ArrayList ifArray = new ArrayList(); - while (cur.position < sigLen && sig.charAt(cur.position) == 'L') - ifArray.add(parseClassType(sig, cur)); - - ClassType[] ifs - = (ClassType[])ifArray.toArray(new ClassType[ifArray.size()]); - return new ClassSignature(tp, superClass, ifs); - } - - private static MethodSignature parseMethodSig(String sig) - throws BadBytecode - { - Cursor cur = new Cursor(); - TypeParameter[] tp = parseTypeParams(sig, cur); - if (sig.charAt(cur.position++) != '(') - throw error(sig); - - ArrayList params = new ArrayList(); - while (sig.charAt(cur.position) != ')') { - Type t = parseType(sig, cur); - params.add(t); - } - - cur.position++; - Type ret = parseType(sig, cur); - int sigLen = sig.length(); - ArrayList exceptions = new ArrayList(); - while (cur.position < sigLen && sig.charAt(cur.position) == '^') { - cur.position++; - ObjectType t = parseObjectType(sig, cur, false); - if (t instanceof ArrayType) - throw error(sig); - - exceptions.add(t); - } - - Type[] p = (Type[])params.toArray(new Type[params.size()]); - ObjectType[] ex = (ObjectType[])exceptions.toArray(new ObjectType[exceptions.size()]); - return new MethodSignature(tp, p, ret, ex); - } - - private static TypeParameter[] parseTypeParams(String sig, Cursor cur) - throws BadBytecode - { - ArrayList typeParam = new ArrayList(); - if (sig.charAt(cur.position) == '<') { - cur.position++; - while (sig.charAt(cur.position) != '>') { - int nameBegin = cur.position; - int nameEnd = cur.indexOf(sig, ':'); - ObjectType classBound = parseObjectType(sig, cur, true); - ArrayList ifBound = new ArrayList(); - while (sig.charAt(cur.position) == ':') { - cur.position++; - ObjectType t = parseObjectType(sig, cur, false); - ifBound.add(t); - } - - TypeParameter p = new TypeParameter(sig, nameBegin, nameEnd, - classBound, (ObjectType[])ifBound.toArray(new ObjectType[ifBound.size()])); - typeParam.add(p); - } - - cur.position++; - } - - return (TypeParameter[])typeParam.toArray(new TypeParameter[typeParam.size()]); - } - - private static ObjectType parseObjectType(String sig, Cursor c, boolean dontThrow) - throws BadBytecode - { - int i; - int begin = c.position; - switch (sig.charAt(begin)) { - case 'L' : - return parseClassType2(sig, c, null); - case 'T' : - i = c.indexOf(sig, ';'); - return new TypeVariable(sig, begin + 1, i); - case '[' : - return parseArray(sig, c); - default : - if (dontThrow) - return null; - else - throw error(sig); - } - } - - private static ClassType parseClassType(String sig, Cursor c) - throws BadBytecode - { - if (sig.charAt(c.position) == 'L') - return parseClassType2(sig, c, null); - else - throw error(sig); - } - - private static ClassType parseClassType2(String sig, Cursor c, ClassType parent) - throws BadBytecode - { - int start = ++c.position; - char t; - do { - t = sig.charAt(c.position++); - } while (t != '$' && t != '<' && t != ';'); - int end = c.position - 1; - TypeArgument[] targs; - if (t == '<') { - targs = parseTypeArgs(sig, c); - t = sig.charAt(c.position++); - } - else - targs = null; - - ClassType thisClass = ClassType.make(sig, start, end, targs, parent); - if (t == '$' || t == '.') { - c.position--; - return parseClassType2(sig, c, thisClass); - } - else - return thisClass; - } - - private static TypeArgument[] parseTypeArgs(String sig, Cursor c) throws BadBytecode { - ArrayList args = new ArrayList(); - char t; - while ((t = sig.charAt(c.position++)) != '>') { - TypeArgument ta; - if (t == '*' ) - ta = new TypeArgument(null, '*'); - else { - if (t != '+' && t != '-') { - t = ' '; - c.position--; - } - - ta = new TypeArgument(parseObjectType(sig, c, false), t); - } - - args.add(ta); - } - - return (TypeArgument[])args.toArray(new TypeArgument[args.size()]); - } - - private static ObjectType parseArray(String sig, Cursor c) throws BadBytecode { - int dim = 1; - while (sig.charAt(++c.position) == '[') - dim++; - - return new ArrayType(dim, parseType(sig, c)); - } - - private static Type parseType(String sig, Cursor c) throws BadBytecode { - Type t = parseObjectType(sig, c, true); - if (t == null) - t = new BaseType(sig.charAt(c.position++)); - - return t; - } - - private static BadBytecode error(String sig) { - return new BadBytecode("bad signature: " + sig); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java deleted file mode 100644 index 3f16225..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -/** - * SourceFile_attribute. - */ -public class SourceFileAttribute extends AttributeInfo { - /** - * The name of this attribute "SourceFile". - */ - public static final String tag = "SourceFile"; - - SourceFileAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Constructs a SourceFile attribute. - * - * @param cp a constant pool table. - * @param filename the name of the source file. - */ - public SourceFileAttribute(ConstPool cp, String filename) { - super(cp, tag); - int index = cp.addUtf8Info(filename); - byte[] bvalue = new byte[2]; - bvalue[0] = (byte)(index >>> 8); - bvalue[1] = (byte)index; - set(bvalue); - } - - /** - * Returns the file name indicated by sourcefile_index. - */ - public String getFileName() { - return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0)); - } - - /** - * Makes a copy. Class names are replaced according to the - * given Map object. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames pairs of replaced and substituted - * class names. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - return new SourceFileAttribute(newCp, getFileName()); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/StackMap.java b/src/com/wenshuo/agent/javassist/bytecode/StackMap.java deleted file mode 100644 index 541d6cd..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/StackMap.java +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.bytecode.StackMapTable.InsertLocal; -import com.wenshuo.agent.javassist.bytecode.StackMapTable.NewRemover; -import com.wenshuo.agent.javassist.bytecode.StackMapTable.Shifter; - -/** - * Another stack_map attribute defined in CLDC 1.1 for J2ME. - * - *

This is an entry in the attributes table of a Code attribute. - * It was introduced by J2ME CLDC 1.1 (JSR 139) for pre-verification. - * - *

According to the CLDC specification, the sizes of some fields are not 16bit - * but 32bit if the code size is more than 64K or the number of the local variables - * is more than 64K. However, for the J2ME CLDC technology, they are always 16bit. - * The implementation of the StackMap class assumes they are 16bit. - * - * @see MethodInfo#doPreverify - * @see StackMapTable - * @since 3.12 - */ -public class StackMap extends AttributeInfo { - /** - * The name of this attribute "StackMap". - */ - public static final String tag = "StackMap"; - - - /** - * Constructs a stack_map attribute. - */ - StackMap(ConstPool cp, byte[] newInfo) { - super(cp, tag, newInfo); - } - - StackMap(ConstPool cp, int name_id, DataInputStream in) - throws IOException - { - super(cp, name_id, in); - } - - /** - * Returns number_of_entries. - */ - public int numOfEntries() { - return ByteArray.readU16bit(info, 0); - } - - /** - * Top_variable_info.tag. - */ - public static final int TOP = 0; - - /** - * Integer_variable_info.tag. - */ - public static final int INTEGER = 1; - - /** - * Float_variable_info.tag. - */ - public static final int FLOAT = 2; - - /** - * Double_variable_info.tag. - */ - public static final int DOUBLE = 3; - - /** - * Long_variable_info.tag. - */ - public static final int LONG = 4; - - /** - * Null_variable_info.tag. - */ - public static final int NULL = 5; - - /** - * UninitializedThis_variable_info.tag. - */ - public static final int THIS = 6; - - /** - * Object_variable_info.tag. - */ - public static final int OBJECT = 7; - - /** - * Uninitialized_variable_info.tag. - */ - public static final int UNINIT = 8; - - /** - * Makes a copy. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - Copier copier = new Copier(this, newCp, classnames); - copier.visit(); - return copier.getStackMap(); - } - - /** - * A code walker for a StackMap attribute. - */ - public static class Walker { - byte[] info; - - /** - * Constructs a walker. - */ - public Walker(StackMap sm) { - info = sm.get(); - } - - /** - * Visits each entry of the stack map frames. - */ - public void visit() { - int num = ByteArray.readU16bit(info, 0); - int pos = 2; - for (int i = 0; i < num; i++) { - int offset = ByteArray.readU16bit(info, pos); - int numLoc = ByteArray.readU16bit(info, pos + 2); - pos = locals(pos + 4, offset, numLoc); - int numStack = ByteArray.readU16bit(info, pos); - pos = stack(pos + 2, offset, numStack); - } - } - - /** - * Invoked when locals of stack_map_frame - * is visited. - */ - public int locals(int pos, int offset, int num) { - return typeInfoArray(pos, offset, num, true); - } - - /** - * Invoked when stack of stack_map_frame - * is visited. - */ - public int stack(int pos, int offset, int num) { - return typeInfoArray(pos, offset, num, false); - } - - /** - * Invoked when an array of verification_type_info is - * visited. - * - * @param num the number of elements. - * @param isLocals true if this array is for locals. - * false if it is for stack. - */ - public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { - for (int k = 0; k < num; k++) - pos = typeInfoArray2(k, pos); - - return pos; - } - - int typeInfoArray2(int k, int pos) { - byte tag = info[pos]; - if (tag == OBJECT) { - int clazz = ByteArray.readU16bit(info, pos + 1); - objectVariable(pos, clazz); - pos += 3; - } - else if (tag == UNINIT) { - int offsetOfNew = ByteArray.readU16bit(info, pos + 1); - uninitialized(pos, offsetOfNew); - pos += 3; - } - else { - typeInfo(pos, tag); - pos++; - } - - return pos; - } - - /** - * Invoked when an element of verification_type_info - * (except Object_variable_info and - * Uninitialized_variable_info) is visited. - */ - public void typeInfo(int pos, byte tag) {} - - /** - * Invoked when an element of type Object_variable_info - * is visited. - */ - public void objectVariable(int pos, int clazz) {} - - /** - * Invoked when an element of type Uninitialized_variable_info - * is visited. - */ - public void uninitialized(int pos, int offset) {} - } - - static class Copier extends Walker { - byte[] dest; - ConstPool srcCp, destCp; - Map classnames; - - Copier(StackMap map, ConstPool newCp, Map classnames) { - super(map); - srcCp = map.getConstPool(); - dest = new byte[info.length]; - destCp = newCp; - this.classnames = classnames; - } - - public void visit() { - int num = ByteArray.readU16bit(info, 0); - ByteArray.write16bit(num, dest, 0); - super.visit(); - } - - public int locals(int pos, int offset, int num) { - ByteArray.write16bit(offset, dest, pos - 4); - return super.locals(pos, offset, num); - } - - public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { - ByteArray.write16bit(num, dest, pos - 2); - return super.typeInfoArray(pos, offset, num, isLocals); - } - - public void typeInfo(int pos, byte tag) { - dest[pos] = tag; - } - - public void objectVariable(int pos, int clazz) { - dest[pos] = OBJECT; - int newClazz = srcCp.copy(clazz, destCp, classnames); - ByteArray.write16bit(newClazz, dest, pos + 1); - } - - public void uninitialized(int pos, int offset) { - dest[pos] = UNINIT; - ByteArray.write16bit(offset, dest, pos + 1); - } - - public StackMap getStackMap() { - return new StackMap(destCp, dest); - } - } - - /** - * Updates this stack map table when a new local variable is inserted - * for a new parameter. - * - * @param index the index of the added local variable. - * @param tag the type tag of that local variable. - * It is available by StackMapTable.typeTagOf(char). - * @param classInfo the index of the CONSTANT_Class_info structure - * in a constant pool table. This should be zero unless the tag - * is ITEM_Object. - * - * @see javassist.CtBehavior#addParameter(javassist.CtClass) - * @see StackMapTable#typeTagOf(char) - * @see ConstPool - */ - public void insertLocal(int index, int tag, int classInfo) - throws BadBytecode - { - byte[] data = new InsertLocal(this, index, tag, classInfo).doit(); - this.set(data); - } - - static class SimpleCopy extends Walker { - Writer writer; - - SimpleCopy(StackMap map) { - super(map); - writer = new Writer(); - } - - byte[] doit() { - visit(); - return writer.toByteArray(); - } - - public void visit() { - int num = ByteArray.readU16bit(info, 0); - writer.write16bit(num); - super.visit(); - } - - public int locals(int pos, int offset, int num) { - writer.write16bit(offset); - return super.locals(pos, offset, num); - } - - public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { - writer.write16bit(num); - return super.typeInfoArray(pos, offset, num, isLocals); - } - - public void typeInfo(int pos, byte tag) { - writer.writeVerifyTypeInfo(tag, 0); - } - - public void objectVariable(int pos, int clazz) { - writer.writeVerifyTypeInfo(OBJECT, clazz); - } - - public void uninitialized(int pos, int offset) { - writer.writeVerifyTypeInfo(UNINIT, offset); - } - } - - static class InsertLocal extends SimpleCopy { - private int varIndex; - private int varTag, varData; - - InsertLocal(StackMap map, int varIndex, int varTag, int varData) { - super(map); - this.varIndex = varIndex; - this.varTag = varTag; - this.varData = varData; - } - - public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { - if (!isLocals || num < varIndex) - return super.typeInfoArray(pos, offset, num, isLocals); - - writer.write16bit(num + 1); - for (int k = 0; k < num; k++) { - if (k == varIndex) - writeVarTypeInfo(); - - pos = typeInfoArray2(k, pos); - } - - if (num == varIndex) - writeVarTypeInfo(); - - return pos; - } - - private void writeVarTypeInfo() { - if (varTag == OBJECT) - writer.writeVerifyTypeInfo(OBJECT, varData); - else if (varTag == UNINIT) - writer.writeVerifyTypeInfo(UNINIT, varData); - else - writer.writeVerifyTypeInfo(varTag, 0); - } - } - - void shiftPc(int where, int gapSize, boolean exclusive) - throws BadBytecode - { - new Shifter(this, where, gapSize, exclusive).visit(); - } - - static class Shifter extends Walker { - private int where, gap; - private boolean exclusive; - - public Shifter(StackMap smt, int where, int gap, boolean exclusive) { - super(smt); - this.where = where; - this.gap = gap; - this.exclusive = exclusive; - } - - public int locals(int pos, int offset, int num) { - if (exclusive ? where <= offset : where < offset) - ByteArray.write16bit(offset + gap, info, pos - 4); - - return super.locals(pos, offset, num); - } - - public void uninitialized(int pos, int offset) { - if (where <= offset) - ByteArray.write16bit(offset + gap, info, pos + 1); - } - } - - /** - * @see CodeIterator.Switcher#adjustOffsets(int, int) - */ - void shiftForSwitch(int where, int gapSize) throws BadBytecode { - new SwitchShifter(this, where, gapSize).visit(); - } - - static class SwitchShifter extends Walker { - private int where, gap; - - public SwitchShifter(StackMap smt, int where, int gap) { - super(smt); - this.where = where; - this.gap = gap; - } - - public int locals(int pos, int offset, int num) { - if (where == pos + offset) - ByteArray.write16bit(offset - gap, info, pos - 4); - else if (where == pos) - ByteArray.write16bit(offset + gap, info, pos - 4); - - return super.locals(pos, offset, num); - } - } - - /** - * Undocumented method. Do not use; internal-use only. - * - *

This method is for javassist.convert.TransformNew. - * It is called to update the stack map when - * the NEW opcode (and the following DUP) is removed. - * - * @param where the position of the removed NEW opcode. - */ - public void removeNew(int where) throws CannotCompileException { - byte[] data = new NewRemover(this, where).doit(); - this.set(data); - } - - static class NewRemover extends SimpleCopy { - int posOfNew; - - NewRemover(StackMap map, int where) { - super(map); - posOfNew = where; - } - - public int stack(int pos, int offset, int num) { - return stackTypeInfoArray(pos, offset, num); - } - - private int stackTypeInfoArray(int pos, int offset, int num) { - int p = pos; - int count = 0; - for (int k = 0; k < num; k++) { - byte tag = info[p]; - if (tag == OBJECT) - p += 3; - else if (tag == UNINIT) { - int offsetOfNew = ByteArray.readU16bit(info, p + 1); - if (offsetOfNew == posOfNew) - count++; - - p += 3; - } - else - p++; - } - - writer.write16bit(num - count); - for (int k = 0; k < num; k++) { - byte tag = info[pos]; - if (tag == OBJECT) { - int clazz = ByteArray.readU16bit(info, pos + 1); - objectVariable(pos, clazz); - pos += 3; - } - else if (tag == UNINIT) { - int offsetOfNew = ByteArray.readU16bit(info, pos + 1); - if (offsetOfNew != posOfNew) - uninitialized(pos, offsetOfNew); - - pos += 3; - } - else { - typeInfo(pos, tag); - pos++; - } - } - - return pos; - } - } - - /** - * Prints this stack map. - */ - public void print(java.io.PrintWriter out) { - new Printer(this, out).print(); - } - - static class Printer extends Walker { - private java.io.PrintWriter writer; - - public Printer(StackMap map, java.io.PrintWriter out) { - super(map); - writer = out; - } - - public void print() { - int num = ByteArray.readU16bit(info, 0); - writer.println(num + " entries"); - visit(); - } - - public int locals(int pos, int offset, int num) { - writer.println(" * offset " + offset); - return super.locals(pos, offset, num); - } - } - - /** - * Internal use only. - */ - public static class Writer { - // see javassist.bytecode.stackmap.MapMaker - - private ByteArrayOutputStream output; - - /** - * Constructs a writer. - */ - public Writer() { - output = new ByteArrayOutputStream(); - } - - /** - * Converts the written data into a byte array. - */ - public byte[] toByteArray() { - return output.toByteArray(); - } - - /** - * Converts to a StackMap attribute. - */ - public StackMap toStackMap(ConstPool cp) { - return new StackMap(cp, output.toByteArray()); - } - - /** - * Writes a union verification_type_info value. - * - * @param data cpool_index or offset. - */ - public void writeVerifyTypeInfo(int tag, int data) { - output.write(tag); - if (tag == StackMap.OBJECT || tag == StackMap.UNINIT) - write16bit(data); - } - - /** - * Writes a 16bit value. - */ - public void write16bit(int value) { - output.write((value >>> 8) & 0xff); - output.write(value & 0xff); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/StackMapTable.java b/src/com/wenshuo/agent/javassist/bytecode/StackMapTable.java deleted file mode 100644 index 5a843fe..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/StackMapTable.java +++ /dev/null @@ -1,1049 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.PrintWriter; -import java.io.IOException; -import java.util.Map; -import com.wenshuo.agent.javassist.CannotCompileException; - -/** - * stack_map attribute. - * - *

This is an entry in the attributes table of a Code attribute. - * It was introduced by J2SE 6 for the verification by - * typechecking. - * - * @see StackMap - * @since 3.4 - */ -public class StackMapTable extends AttributeInfo { - /** - * The name of this attribute "StackMapTable". - */ - public static final String tag = "StackMapTable"; - - /** - * Constructs a stack_map attribute. - */ - StackMapTable(ConstPool cp, byte[] newInfo) { - super(cp, tag, newInfo); - } - - StackMapTable(ConstPool cp, int name_id, DataInputStream in) - throws IOException - { - super(cp, name_id, in); - } - - /** - * Makes a copy. - * - * @exception RuntimeCopyException if a BadBytecode - * exception is thrown while copying, - * it is converted into - * RuntimeCopyException. - * - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) - throws RuntimeCopyException - { - try { - return new StackMapTable(newCp, - new Copier(this.constPool, info, newCp, classnames).doit()); - } - catch (BadBytecode e) { - throw new RuntimeCopyException("bad bytecode. fatal?"); - } - } - - /** - * An exception that may be thrown by copy() - * in StackMapTable. - */ - public static class RuntimeCopyException extends RuntimeException { - /** - * Constructs an exception. - */ - public RuntimeCopyException(String s) { - super(s); - } - } - - void write(DataOutputStream out) throws IOException { - super.write(out); - } - - /** - * Top_variable_info.tag. - */ - public static final int TOP = 0; - - /** - * Integer_variable_info.tag. - */ - public static final int INTEGER = 1; - - /** - * Float_variable_info.tag. - */ - public static final int FLOAT = 2; - - /** - * Double_variable_info.tag. - */ - public static final int DOUBLE = 3; - - /** - * Long_variable_info.tag. - */ - public static final int LONG = 4; - - /** - * Null_variable_info.tag. - */ - public static final int NULL = 5; - - /** - * UninitializedThis_variable_info.tag. - */ - public static final int THIS = 6; - - /** - * Object_variable_info.tag. - */ - public static final int OBJECT = 7; - - /** - * Uninitialized_variable_info.tag. - */ - public static final int UNINIT = 8; - - /** - * A code walker for a StackMapTable attribute. - */ - public static class Walker { - byte[] info; - int numOfEntries; - - /** - * Constructs a walker. - * - * @param smt the StackMapTable that this walker - * walks around. - */ - public Walker(StackMapTable smt) { - this(smt.get()); - } - - /** - * Constructs a walker. - * - * @param data the info field of the - * attribute_info structure. - * It can be obtained by get() - * in the AttributeInfo class. - */ - public Walker(byte[] data) { - info = data; - numOfEntries = ByteArray.readU16bit(data, 0); - } - - /** - * Returns the number of the entries. - */ - public final int size() { return numOfEntries; } - - /** - * Visits each entry of the stack map frames. - */ - public void parse() throws BadBytecode { - int n = numOfEntries; - int pos = 2; - for (int i = 0; i < n; i++) - pos = stackMapFrames(pos, i); - } - - /** - * Invoked when the next entry of the stack map frames is visited. - * - * @param pos the position of the frame in the info - * field of attribute_info structure. - * @param nth the frame is the N-th - * (0, 1st, 2nd, 3rd, 4th, ...) entry. - * @return the position of the next frame. - */ - int stackMapFrames(int pos, int nth) throws BadBytecode { - int type = info[pos] & 0xff; - if (type < 64) { - sameFrame(pos, type); - pos++; - } - else if (type < 128) - pos = sameLocals(pos, type); - else if (type < 247) - throw new BadBytecode("bad frame_type in StackMapTable"); - else if (type == 247) // SAME_LOCALS_1_STACK_ITEM_EXTENDED - pos = sameLocals(pos, type); - else if (type < 251) { - int offset = ByteArray.readU16bit(info, pos + 1); - chopFrame(pos, offset, 251 - type); - pos += 3; - } - else if (type == 251) { // SAME_FRAME_EXTENDED - int offset = ByteArray.readU16bit(info, pos + 1); - sameFrame(pos, offset); - pos += 3; - } - else if (type < 255) - pos = appendFrame(pos, type); - else // FULL_FRAME - pos = fullFrame(pos); - - return pos; - } - - /** - * Invoked if the visited frame is a same_frame or - * a same_frame_extended. - * - * @param pos the position of this frame in the info - * field of attribute_info structure. - * @param offsetDelta - */ - public void sameFrame(int pos, int offsetDelta) throws BadBytecode {} - - private int sameLocals(int pos, int type) throws BadBytecode { - int top = pos; - int offset; - if (type < 128) - offset = type - 64; - else { // type == 247 - offset = ByteArray.readU16bit(info, pos + 1); - pos += 2; - } - - int tag = info[pos + 1] & 0xff; - int data = 0; - if (tag == OBJECT || tag == UNINIT) { - data = ByteArray.readU16bit(info, pos + 2); - objectOrUninitialized(tag, data, pos + 2); - pos += 2; - } - - sameLocals(top, offset, tag, data); - return pos + 2; - } - - /** - * Invoked if the visited frame is a same_locals_1_stack_item_frame - * or a same_locals_1_stack_item_frame_extended. - * - * @param pos the position. - * @param offsetDelta - * @param stackTag stack[0].tag. - * @param stackData stack[0].cpool_index - * if the tag is OBJECT, - * or stack[0].offset - * if the tag is UNINIT. - */ - public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) - throws BadBytecode {} - - /** - * Invoked if the visited frame is a chop_frame. - * - * @param pos the position. - * @param offsetDelta - * @param k the k last locals are absent. - */ - public void chopFrame(int pos, int offsetDelta, int k) throws BadBytecode {} - - private int appendFrame(int pos, int type) throws BadBytecode { - int k = type - 251; - int offset = ByteArray.readU16bit(info, pos + 1); - int[] tags = new int[k]; - int[] data = new int[k]; - int p = pos + 3; - for (int i = 0; i < k; i++) { - int tag = info[p] & 0xff; - tags[i] = tag; - if (tag == OBJECT || tag == UNINIT) { - data[i] = ByteArray.readU16bit(info, p + 1); - objectOrUninitialized(tag, data[i], p + 1); - p += 3; - } - else { - data[i] = 0; - p++; - } - } - - appendFrame(pos, offset, tags, data); - return p; - } - - /** - * Invoked if the visited frame is a append_frame. - * - * @param pos the position. - * @param offsetDelta - * @param tags locals[i].tag. - * @param data locals[i].cpool_index - * or locals[i].offset. - */ - public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) - throws BadBytecode {} - - private int fullFrame(int pos) throws BadBytecode { - int offset = ByteArray.readU16bit(info, pos + 1); - int numOfLocals = ByteArray.readU16bit(info, pos + 3); - int[] localsTags = new int[numOfLocals]; - int[] localsData = new int[numOfLocals]; - int p = verifyTypeInfo(pos + 5, numOfLocals, localsTags, localsData); - int numOfItems = ByteArray.readU16bit(info, p); - int[] itemsTags = new int[numOfItems]; - int[] itemsData = new int[numOfItems]; - p = verifyTypeInfo(p + 2, numOfItems, itemsTags, itemsData); - fullFrame(pos, offset, localsTags, localsData, itemsTags, itemsData); - return p; - } - - /** - * Invoked if the visited frame is full_frame. - * - * @param pos the position. - * @param offsetDelta - * @param localTags locals[i].tag - * @param localData locals[i].cpool_index - * or locals[i].offset - * @param stackTags stack[i].tag - * @param stackData stack[i].cpool_index - * or stack[i].offset - */ - public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, - int[] stackTags, int[] stackData) - throws BadBytecode {} - - private int verifyTypeInfo(int pos, int n, int[] tags, int[] data) { - for (int i = 0; i < n; i++) { - int tag = info[pos++] & 0xff; - tags[i] = tag; - if (tag == OBJECT || tag == UNINIT) { - data[i] = ByteArray.readU16bit(info, pos); - objectOrUninitialized(tag, data[i], pos); - pos += 2; - } - } - - return pos; - } - - /** - * Invoked if Object_variable_info - * or Uninitialized_variable_info is visited. - * - * @param tag OBJECT or UNINIT. - * @param data the value of cpool_index or offset. - * @param pos the position of cpool_index or offset. - */ - public void objectOrUninitialized(int tag, int data, int pos) {} - } - - static class SimpleCopy extends Walker { - private Writer writer; - - public SimpleCopy(byte[] data) { - super(data); - writer = new Writer(data.length); - } - - public byte[] doit() throws BadBytecode { - parse(); - return writer.toByteArray(); - } - - public void sameFrame(int pos, int offsetDelta) { - writer.sameFrame(offsetDelta); - } - - public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { - writer.sameLocals(offsetDelta, stackTag, copyData(stackTag, stackData)); - } - - public void chopFrame(int pos, int offsetDelta, int k) { - writer.chopFrame(offsetDelta, k); - } - - public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { - writer.appendFrame(offsetDelta, tags, copyData(tags, data)); - } - - public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, - int[] stackTags, int[] stackData) { - writer.fullFrame(offsetDelta, localTags, copyData(localTags, localData), - stackTags, copyData(stackTags, stackData)); - } - - protected int copyData(int tag, int data) { - return data; - } - - protected int[] copyData(int[] tags, int[] data) { - return data; - } - } - - static class Copier extends SimpleCopy { - private ConstPool srcPool, destPool; - private Map classnames; - - public Copier(ConstPool src, byte[] data, ConstPool dest, Map names) { - super(data); - srcPool = src; - destPool = dest; - classnames = names; - } - - protected int copyData(int tag, int data) { - if (tag == OBJECT) - return srcPool.copy(data, destPool, classnames); - else - return data; - } - - protected int[] copyData(int[] tags, int[] data) { - int[] newData = new int[data.length]; - for (int i = 0; i < data.length; i++) - if (tags[i] == OBJECT) - newData[i] = srcPool.copy(data[i], destPool, classnames); - else - newData[i] = data[i]; - - return newData; - } - } - - /** - * Updates this stack map table when a new local variable is inserted - * for a new parameter. - * - * @param index the index of the added local variable. - * @param tag the type tag of that local variable. - * @param classInfo the index of the CONSTANT_Class_info structure - * in a constant pool table. This should be zero unless the tag - * is ITEM_Object. - * - * @see javassist.CtBehavior#addParameter(javassist.CtClass) - * @see #typeTagOf(char) - * @see ConstPool - */ - public void insertLocal(int index, int tag, int classInfo) - throws BadBytecode - { - byte[] data = new InsertLocal(this.get(), index, tag, classInfo).doit(); - this.set(data); - } - - /** - * Returns the tag of the type specified by the - * descriptor. This method returns INTEGER - * unless the descriptor is either D (double), F (float), - * J (long), L (class type), or [ (array). - * - * @param descriptor the type descriptor. - * @see Descriptor - */ - public static int typeTagOf(char descriptor) { - switch (descriptor) { - case 'D' : - return DOUBLE; - case 'F' : - return FLOAT; - case 'J' : - return LONG; - case 'L' : - case '[' : - return OBJECT; - // case 'V' : - default : - return INTEGER; - } - } - - /* This implementation assumes that a local variable initially - * holding a parameter value is never changed to be a different - * type. - * - */ - static class InsertLocal extends SimpleCopy { - private int varIndex; - private int varTag, varData; - - public InsertLocal(byte[] data, int varIndex, int varTag, int varData) { - super(data); - this.varIndex = varIndex; - this.varTag = varTag; - this.varData = varData; - } - - public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, - int[] stackTags, int[] stackData) { - int len = localTags.length; - if (len < varIndex) { - super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); - return; - } - - int typeSize = (varTag == LONG || varTag == DOUBLE) ? 2 : 1; - int[] localTags2 = new int[len + typeSize]; - int[] localData2 = new int[len + typeSize]; - int index = varIndex; - int j = 0; - for (int i = 0; i < len; i++) { - if (j == index) - j += typeSize; - - localTags2[j] = localTags[i]; - localData2[j++] = localData[i]; - } - - localTags2[index] = varTag; - localData2[index] = varData; - if (typeSize > 1) { - localTags2[index + 1] = TOP; - localData2[index + 1] = 0; - } - - super.fullFrame(pos, offsetDelta, localTags2, localData2, stackTags, stackData); - } - } - - /** - * A writer of stack map tables. - */ - public static class Writer { - ByteArrayOutputStream output; - int numOfEntries; - - /** - * Constructs a writer. - * @param size the initial buffer size. - */ - public Writer(int size) { - output = new ByteArrayOutputStream(size); - numOfEntries = 0; - output.write(0); // u2 number_of_entries - output.write(0); - } - - /** - * Returns the stack map table written out. - */ - public byte[] toByteArray() { - byte[] b = output.toByteArray(); - ByteArray.write16bit(numOfEntries, b, 0); - return b; - } - - /** - * Constructs and a return a stack map table containing - * the written stack map entries. - * - * @param cp the constant pool used to write - * the stack map entries. - */ - public StackMapTable toStackMapTable(ConstPool cp) { - return new StackMapTable(cp, toByteArray()); - } - - /** - * Writes a same_frame or a same_frame_extended. - */ - public void sameFrame(int offsetDelta) { - numOfEntries++; - if (offsetDelta < 64) - output.write(offsetDelta); - else { - output.write(251); // SAME_FRAME_EXTENDED - write16(offsetDelta); - } - } - - /** - * Writes a same_locals_1_stack_item - * or a same_locals_1_stack_item_extended. - * - * @param tag stack[0].tag. - * @param data stack[0].cpool_index - * if the tag is OBJECT, - * or stack[0].offset - * if the tag is UNINIT. - * Otherwise, this parameter is not used. - */ - public void sameLocals(int offsetDelta, int tag, int data) { - numOfEntries++; - if (offsetDelta < 64) - output.write(offsetDelta + 64); - else { - output.write(247); // SAME_LOCALS_1_STACK_ITEM_EXTENDED - write16(offsetDelta); - } - - writeTypeInfo(tag, data); - } - - /** - * Writes a chop_frame. - * - * @param k the number of absent locals. 1, 2, or 3. - */ - public void chopFrame(int offsetDelta, int k) { - numOfEntries++; - output.write(251 - k); - write16(offsetDelta); - } - - /** - * Writes a append_frame. The number of the appended - * locals is specified by the length of tags. - * - * @param tags locals[].tag. - * The length of this array must be - * either 1, 2, or 3. - * @param data locals[].cpool_index - * if the tag is OBJECT, - * or locals[].offset - * if the tag is UNINIT. - * Otherwise, this parameter is not used. - */ - public void appendFrame(int offsetDelta, int[] tags, int[] data) { - numOfEntries++; - int k = tags.length; // k is 1, 2, or 3 - output.write(k + 251); - write16(offsetDelta); - for (int i = 0; i < k; i++) - writeTypeInfo(tags[i], data[i]); - } - - /** - * Writes a full_frame. - * number_of_locals and number_of_stack_items - * are specified by the the length of localTags and - * stackTags. - * - * @param localTags locals[].tag. - * @param localData locals[].cpool_index - * if the tag is OBJECT, - * or locals[].offset - * if the tag is UNINIT. - * Otherwise, this parameter is not used. - * @param stackTags stack[].tag. - * @param stackData stack[].cpool_index - * if the tag is OBJECT, - * or stack[].offset - * if the tag is UNINIT. - * Otherwise, this parameter is not used. - */ - public void fullFrame(int offsetDelta, int[] localTags, int[] localData, - int[] stackTags, int[] stackData) { - numOfEntries++; - output.write(255); // FULL_FRAME - write16(offsetDelta); - int n = localTags.length; - write16(n); - for (int i = 0; i < n; i++) - writeTypeInfo(localTags[i], localData[i]); - - n = stackTags.length; - write16(n); - for (int i = 0; i < n; i++) - writeTypeInfo(stackTags[i], stackData[i]); - } - - private void writeTypeInfo(int tag, int data) { - output.write(tag); - if (tag == OBJECT || tag == UNINIT) - write16(data); - } - - private void write16(int value) { - output.write((value >>> 8) & 0xff); - output.write(value & 0xff); - } - } - - /** - * Prints the stack table map. - */ - public void println(PrintWriter w) { - Printer.print(this, w); - } - - /** - * Prints the stack table map. - * - * @param ps a print stream such as System.out. - */ - public void println(java.io.PrintStream ps) { - Printer.print(this, new java.io.PrintWriter(ps, true)); - } - - static class Printer extends Walker { - private PrintWriter writer; - private int offset; - - /** - * Prints the stack table map. - */ - public static void print(StackMapTable smt, PrintWriter writer) { - try { - new Printer(smt.get(), writer).parse(); - } - catch (BadBytecode e) { - writer.println(e.getMessage()); - } - } - - Printer(byte[] data, PrintWriter pw) { - super(data); - writer = pw; - offset = -1; - } - - public void sameFrame(int pos, int offsetDelta) { - offset += offsetDelta + 1; - writer.println(offset + " same frame: " + offsetDelta); - } - - public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { - offset += offsetDelta + 1; - writer.println(offset + " same locals: " + offsetDelta); - printTypeInfo(stackTag, stackData); - } - - public void chopFrame(int pos, int offsetDelta, int k) { - offset += offsetDelta + 1; - writer.println(offset + " chop frame: " + offsetDelta + ", " + k + " last locals"); - } - - public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { - offset += offsetDelta + 1; - writer.println(offset + " append frame: " + offsetDelta); - for (int i = 0; i < tags.length; i++) - printTypeInfo(tags[i], data[i]); - } - - public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, - int[] stackTags, int[] stackData) { - offset += offsetDelta + 1; - writer.println(offset + " full frame: " + offsetDelta); - writer.println("[locals]"); - for (int i = 0; i < localTags.length; i++) - printTypeInfo(localTags[i], localData[i]); - - writer.println("[stack]"); - for (int i = 0; i < stackTags.length; i++) - printTypeInfo(stackTags[i], stackData[i]); - } - - private void printTypeInfo(int tag, int data) { - String msg = null; - switch (tag) { - case TOP : - msg = "top"; - break; - case INTEGER : - msg = "integer"; - break; - case FLOAT : - msg = "float"; - break; - case DOUBLE : - msg = "double"; - break; - case LONG : - msg = "long"; - break; - case NULL : - msg = "null"; - break; - case THIS : - msg = "this"; - break; - case OBJECT : - msg = "object (cpool_index " + data + ")"; - break; - case UNINIT : - msg = "uninitialized (offset " + data + ")"; - break; - } - - writer.print(" "); - writer.println(msg); - } - } - - void shiftPc(int where, int gapSize, boolean exclusive) - throws BadBytecode - { - new OffsetShifter(this, where, gapSize).parse(); - new Shifter(this, where, gapSize, exclusive).doit(); - } - - static class OffsetShifter extends Walker { - int where, gap; - - public OffsetShifter(StackMapTable smt, int where, int gap) { - super(smt); - this.where = where; - this.gap = gap; - } - - public void objectOrUninitialized(int tag, int data, int pos) { - if (tag == UNINIT) - if (where <= data) - ByteArray.write16bit(data + gap, info, pos); - } - } - - static class Shifter extends Walker { - private StackMapTable stackMap; - int where, gap; - int position; - byte[] updatedInfo; - boolean exclusive; - - public Shifter(StackMapTable smt, int where, int gap, boolean exclusive) { - super(smt); - stackMap = smt; - this.where = where; - this.gap = gap; - this.position = 0; - this.updatedInfo = null; - this.exclusive = exclusive; - } - - public void doit() throws BadBytecode { - parse(); - if (updatedInfo != null) - stackMap.set(updatedInfo); - } - - public void sameFrame(int pos, int offsetDelta) { - update(pos, offsetDelta, 0, 251); - } - - public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { - update(pos, offsetDelta, 64, 247); - } - - void update(int pos, int offsetDelta, int base, int entry) { - int oldPos = position; - position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); - boolean match; - if (exclusive) - match = oldPos < where && where <= position; - else - match = oldPos <= where && where < position; - - if (match) { - int newDelta = offsetDelta + gap; - position += gap; - if (newDelta < 64) - info[pos] = (byte)(newDelta + base); - else if (offsetDelta < 64) { - byte[] newinfo = insertGap(info, pos, 2); - newinfo[pos] = (byte)entry; - ByteArray.write16bit(newDelta, newinfo, pos + 1); - updatedInfo = newinfo; - } - else - ByteArray.write16bit(newDelta, info, pos + 1); - } - } - - static byte[] insertGap(byte[] info, int where, int gap) { - int len = info.length; - byte[] newinfo = new byte[len + gap]; - for (int i = 0; i < len; i++) - newinfo[i + (i < where ? 0 : gap)] = info[i]; - - return newinfo; - } - - public void chopFrame(int pos, int offsetDelta, int k) { - update(pos, offsetDelta); - } - - public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { - update(pos, offsetDelta); - } - - public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, - int[] stackTags, int[] stackData) { - update(pos, offsetDelta); - } - - void update(int pos, int offsetDelta) { - int oldPos = position; - position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); - boolean match; - if (exclusive) - match = oldPos < where && where <= position; - else - match = oldPos <= where && where < position; - - if (match) { - int newDelta = offsetDelta + gap; - ByteArray.write16bit(newDelta, info, pos + 1); - position += gap; - } - } - } - - /** - * @see CodeIterator.Switcher#adjustOffsets(int, int) - */ - void shiftForSwitch(int where, int gapSize) throws BadBytecode { - new SwitchShifter(this, where, gapSize).doit(); - } - - static class SwitchShifter extends Shifter { - SwitchShifter(StackMapTable smt, int where, int gap) { - super(smt, where, gap, false); - } - - void update(int pos, int offsetDelta, int base, int entry) { - int oldPos = position; - position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); - int newDelta = offsetDelta; - if (where == position) - newDelta = offsetDelta - gap; - else if (where == oldPos) - newDelta = offsetDelta + gap; - else - return; - - if (offsetDelta < 64) - if (newDelta < 64) - info[pos] = (byte)(newDelta + base); - else { - byte[] newinfo = insertGap(info, pos, 2); - newinfo[pos] = (byte)entry; - ByteArray.write16bit(newDelta, newinfo, pos + 1); - updatedInfo = newinfo; - } - else - if (newDelta < 64) { - byte[] newinfo = deleteGap(info, pos, 2); - newinfo[pos] = (byte)(newDelta + base); - updatedInfo = newinfo; - } - else - ByteArray.write16bit(newDelta, info, pos + 1); - } - - static byte[] deleteGap(byte[] info, int where, int gap) { - where += gap; - int len = info.length; - byte[] newinfo = new byte[len - gap]; - for (int i = 0; i < len; i++) - newinfo[i - (i < where ? 0 : gap)] = info[i]; - - return newinfo; - } - - void update(int pos, int offsetDelta) { - int oldPos = position; - position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); - int newDelta = offsetDelta; - if (where == position) - newDelta = offsetDelta - gap; - else if (where == oldPos) - newDelta = offsetDelta + gap; - else - return; - - ByteArray.write16bit(newDelta, info, pos + 1); - } - } - - /** - * Undocumented method. Do not use; internal-use only. - * - *

This method is for javassist.convert.TransformNew. - * It is called to update the stack map table when - * the NEW opcode (and the following DUP) is removed. - * - * @param where the position of the removed NEW opcode. - */ - public void removeNew(int where) throws CannotCompileException { - try { - byte[] data = new NewRemover(this.get(), where).doit(); - this.set(data); - } - catch (BadBytecode e) { - throw new CannotCompileException("bad stack map table", e); - } - } - - static class NewRemover extends SimpleCopy { - int posOfNew; - - public NewRemover(byte[] data, int pos) { - super(data); - posOfNew = pos; - } - - public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { - if (stackTag == UNINIT && stackData == posOfNew) - super.sameFrame(pos, offsetDelta); - else - super.sameLocals(pos, offsetDelta, stackTag, stackData); - } - - public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, - int[] stackTags, int[] stackData) { - int n = stackTags.length - 1; - for (int i = 0; i < n; i++) - if (stackTags[i] == UNINIT && stackData[i] == posOfNew - && stackTags[i + 1] == UNINIT && stackData[i + 1] == posOfNew) { - n++; - int[] stackTags2 = new int[n - 2]; - int[] stackData2 = new int[n - 2]; - int k = 0; - for (int j = 0; j < n; j++) - if (j == i) - j++; - else { - stackTags2[k] = stackTags[j]; - stackData2[k++] = stackData[j]; - } - - stackTags = stackTags2; - stackData = stackData2; - break; - } - - super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java deleted file mode 100644 index 52484b7..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.Map; - -/** - * Synthetic_attribute. - */ -public class SyntheticAttribute extends AttributeInfo { - /** - * The name of this attribute "Synthetic". - */ - public static final String tag = "Synthetic"; - - SyntheticAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Constructs a Synthetic attribute. - * - * @param cp a constant pool table. - */ - public SyntheticAttribute(ConstPool cp) { - super(cp, tag, new byte[0]); - } - - /** - * Makes a copy. - * - * @param newCp the constant pool table used by the new copy. - * @param classnames should be null. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - return new SyntheticAttribute(newCp); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java b/src/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java deleted file mode 100644 index 29f075f..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java +++ /dev/null @@ -1,360 +0,0 @@ -package com.wenshuo.agent.javassist.bytecode; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import com.wenshuo.agent.javassist.bytecode.annotation.TypeAnnotationsWriter; - -/** - * A class representing - * {@code RuntimeVisibleTypeAnnotations} attribute and - * {@code RuntimeInvisibleTypeAnnotations} attribute. - * - * @since 3.19 - */ -public class TypeAnnotationsAttribute extends AttributeInfo { - /** - * The name of the {@code RuntimeVisibleTypeAnnotations} attribute. - */ - public static final String visibleTag = "RuntimeVisibleTypeAnnotations"; - - /** - * The name of the {@code RuntimeInvisibleTypeAnnotations} attribute. - */ - public static final String invisibleTag = "RuntimeInvisibleTypeAnnotations"; - - /** - * Constructs a Runtime(In)VisibleTypeAnnotations_attribute. - * - * @param cp constant pool - * @param attrname attribute name (visibleTag or - * invisibleTag). - * @param info the contents of this attribute. It does not - * include attribute_name_index or - * attribute_length. - */ - public TypeAnnotationsAttribute(ConstPool cp, String attrname, byte[] info) { - super(cp, attrname, info); - } - - /** - * @param n the attribute name. - */ - TypeAnnotationsAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Returns num_annotations. - */ - public int numAnnotations() { - return ByteArray.readU16bit(info, 0); - } - - /** - * Copies this attribute and returns a new copy. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) { - Copier copier = new Copier(info, constPool, newCp, classnames); - try { - copier.annotationArray(); - return new TypeAnnotationsAttribute(newCp, getName(), copier.close()); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * @param oldname a JVM class name. - * @param newname a JVM class name. - */ - void renameClass(String oldname, String newname) { - HashMap map = new HashMap(); - map.put(oldname, newname); - renameClass(map); - } - - void renameClass(Map classnames) { - Renamer renamer = new Renamer(info, getConstPool(), classnames); - try { - renamer.annotationArray(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - void getRefClasses(Map classnames) { renameClass(classnames); } - - /** - * To visit each elements of the type annotation attribute, - * call {@code annotationArray()}. - * - * @see #annotationArray() - */ - static class TAWalker extends AnnotationsAttribute.Walker { - SubWalker subWalker; - - TAWalker(byte[] attrInfo) { - super(attrInfo); - subWalker = new SubWalker(attrInfo); - } - - int annotationArray(int pos, int num) throws Exception { - for (int i = 0; i < num; i++) { - int targetType = info[pos] & 0xff; - pos = subWalker.targetInfo(pos + 1, targetType); - pos = subWalker.typePath(pos); - pos = annotation(pos); - } - - return pos; - } - } - - static class SubWalker { - byte[] info; - - SubWalker(byte[] attrInfo) { - info = attrInfo; - } - - final int targetInfo(int pos, int type) throws Exception { - switch (type) { - case 0x00: - case 0x01: { - int index = info[pos] & 0xff; - typeParameterTarget(pos, type, index); - return pos + 1; } - case 0x10: { - int index = ByteArray.readU16bit(info, pos); - supertypeTarget(pos, index); - return pos + 2; } - case 0x11: - case 0x12: { - int param = info[pos] & 0xff; - int bound = info[pos + 1] & 0xff; - typeParameterBoundTarget(pos, type, param, bound); - return pos + 2; } - case 0x13: - case 0x14: - case 0x15: - emptyTarget(pos, type); - return pos; - case 0x16: { - int index = info[pos] & 0xff; - formalParameterTarget(pos, index); - return pos + 1; } - case 0x17: { - int index = ByteArray.readU16bit(info, pos); - throwsTarget(pos, index); - return pos + 2; } - case 0x40: - case 0x41: { - int len = ByteArray.readU16bit(info, pos); - return localvarTarget(pos + 2, type, len); } - case 0x42: { - int index = ByteArray.readU16bit(info, pos); - catchTarget(pos, index); - return pos + 2; } - case 0x43: - case 0x44: - case 0x45: - case 0x46: { - int offset = ByteArray.readU16bit(info, pos); - offsetTarget(pos, type, offset); - return pos + 2; } - case 0x47: - case 0x48: - case 0x49: - case 0x4a: - case 0x4b: { - int offset = ByteArray.readU16bit(info, pos); - int index = info[pos + 2] & 0xff; - typeArgumentTarget(pos, type, offset, index); - return pos + 3; } - default: - throw new RuntimeException("invalid target type: " + type); - } - } - - void typeParameterTarget(int pos, int targetType, int typeParameterIndex) - throws Exception {} - - void supertypeTarget(int pos, int superTypeIndex) throws Exception {} - - void typeParameterBoundTarget(int pos, int targetType, int typeParameterIndex, - int boundIndex) throws Exception {} - - void emptyTarget(int pos, int targetType) throws Exception {} - - void formalParameterTarget(int pos, int formalParameterIndex) throws Exception {} - - void throwsTarget(int pos, int throwsTypeIndex) throws Exception {} - - int localvarTarget(int pos, int targetType, int tableLength) throws Exception { - for (int i = 0; i < tableLength; i++) { - int start = ByteArray.readU16bit(info, pos); - int length = ByteArray.readU16bit(info, pos + 2); - int index = ByteArray.readU16bit(info, pos + 4); - localvarTarget(pos, targetType, start, length, index); - pos += 6; - } - - return pos; - } - - void localvarTarget(int pos, int targetType, int startPc, int length, int index) - throws Exception {} - - void catchTarget(int pos, int exceptionTableIndex) throws Exception {} - - void offsetTarget(int pos, int targetType, int offset) throws Exception {} - - void typeArgumentTarget(int pos, int targetType, int offset, int typeArgumentIndex) - throws Exception {} - - final int typePath(int pos) throws Exception { - int len = info[pos++] & 0xff; - return typePath(pos, len); - } - - int typePath(int pos, int pathLength) throws Exception { - for (int i = 0; i < pathLength; i++) { - int kind = info[pos] & 0xff; - int index = info[pos + 1] & 0xff; - typePath(pos, kind, index); - pos += 2; - } - - return pos; - } - - void typePath(int pos, int typePathKind, int typeArgumentIndex) throws Exception {} - } - - static class Renamer extends AnnotationsAttribute.Renamer { - SubWalker sub; - - Renamer(byte[] attrInfo, ConstPool cp, Map map) { - super(attrInfo, cp, map); - sub = new SubWalker(attrInfo); - } - - int annotationArray(int pos, int num) throws Exception { - for (int i = 0; i < num; i++) { - int targetType = info[pos] & 0xff; - pos = sub.targetInfo(pos + 1, targetType); - pos = sub.typePath(pos); - pos = annotation(pos); - } - - return pos; - } - } - - static class Copier extends AnnotationsAttribute.Copier { - SubCopier sub; - - Copier(byte[] attrInfo, ConstPool src, ConstPool dest, Map map) { - super(attrInfo, src, dest, map, false); - TypeAnnotationsWriter w = new TypeAnnotationsWriter(output, dest); - writer = w; - sub = new SubCopier(attrInfo, src, dest, map, w); - } - - int annotationArray(int pos, int num) throws Exception { - writer.numAnnotations(num); - for (int i = 0; i < num; i++) { - int targetType = info[pos] & 0xff; - pos = sub.targetInfo(pos + 1, targetType); - pos = sub.typePath(pos); - pos = annotation(pos); - } - - return pos; - } - } - - static class SubCopier extends SubWalker { - ConstPool srcPool, destPool; - Map classnames; - TypeAnnotationsWriter writer; - - SubCopier(byte[] attrInfo, ConstPool src, ConstPool dest, Map map, - TypeAnnotationsWriter w) - { - super(attrInfo); - srcPool = src; - destPool = dest; - classnames = map; - writer = w; - } - - void typeParameterTarget(int pos, int targetType, int typeParameterIndex) - throws Exception - { - writer.typeParameterTarget(targetType, typeParameterIndex); - } - - void supertypeTarget(int pos, int superTypeIndex) throws Exception { - writer.supertypeTarget(superTypeIndex); - } - - void typeParameterBoundTarget(int pos, int targetType, int typeParameterIndex, - int boundIndex) - throws Exception - { - writer.typeParameterBoundTarget(targetType, typeParameterIndex, boundIndex); - } - - void emptyTarget(int pos, int targetType) throws Exception { - writer.emptyTarget(targetType); - } - - void formalParameterTarget(int pos, int formalParameterIndex) throws Exception { - writer.formalParameterTarget(formalParameterIndex); - } - - void throwsTarget(int pos, int throwsTypeIndex) throws Exception { - writer.throwsTarget(throwsTypeIndex); - } - - int localvarTarget(int pos, int targetType, int tableLength) throws Exception { - writer.localVarTarget(targetType, tableLength); - return super.localvarTarget(pos, targetType, tableLength); - } - - void localvarTarget(int pos, int targetType, int startPc, int length, int index) - throws Exception - { - writer.localVarTargetTable(startPc, length, index); - } - - void catchTarget(int pos, int exceptionTableIndex) throws Exception { - writer.catchTarget(exceptionTableIndex); - } - - void offsetTarget(int pos, int targetType, int offset) throws Exception { - writer.offsetTarget(targetType, offset); - } - - void typeArgumentTarget(int pos, int targetType, int offset, int typeArgumentIndex) - throws Exception - { - writer.typeArgumentTarget(targetType, offset, typeArgumentIndex); - } - - int typePath(int pos, int pathLength) throws Exception { - writer.typePath(pathLength); - return super.typePath(pos, pathLength); - } - - void typePath(int pos, int typePathKind, int typeArgumentIndex) throws Exception { - writer.typePathPath(typePathKind, typeArgumentIndex); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java deleted file mode 100644 index e70d005..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import java.util.Iterator; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.AccessFlag; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.CodeAttribute; -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.bytecode.ExceptionTable; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; -import com.wenshuo.agent.javassist.bytecode.Opcode; - -/** - * A data-flow analyzer that determines the type state of the stack and local - * variable table at every reachable instruction in a method. During analysis, - * bytecode verification is performed in a similar manner to that described - * in the JVM specification. - * - *

Example:

- * - *
- * // Method to analyze
- * public Object doSomething(int x) {
- *     Number n;
- *     if (x < 5) {
- *        n = new Double(0);
- *     } else {
- *        n = new Long(0);
- *     }
- *
- *     return n;
- * }
- *
- * // Which compiles to:
- * // 0:   iload_1
- * // 1:   iconst_5
- * // 2:   if_icmpge   17
- * // 5:   new #18; //class java/lang/Double
- * // 8:   dup
- * // 9:   dconst_0
- * // 10:  invokespecial   #44; //Method java/lang/Double."<init>":(D)V
- * // 13:  astore_2
- * // 14:  goto    26
- * // 17:  new #16; //class java/lang/Long
- * // 20:  dup
- * // 21:  lconst_1
- * // 22:  invokespecial   #47; //Method java/lang/Long."<init>":(J)V
- * // 25:  astore_2
- * // 26:  aload_2
- * // 27:  areturn
- *
- * public void analyzeIt(CtClass clazz, MethodInfo method) {
- *     Analyzer analyzer = new Analyzer();
- *     Frame[] frames = analyzer.analyze(clazz, method);
- *     frames[0].getLocal(0).getCtClass(); // returns clazz;
- *     frames[0].getLocal(1).getCtClass(); // returns java.lang.String
- *     frames[1].peek(); // returns Type.INTEGER
- *     frames[27].peek().getCtClass(); // returns java.lang.Number
- * }
- * 
- * - * @see FramePrinter - * @author Jason T. Greene - */ -public class Analyzer implements Opcode { - private final SubroutineScanner scanner = new SubroutineScanner(); - private CtClass clazz; - private ExceptionInfo[] exceptions; - private Frame[] frames; - private Subroutine[] subroutines; - - private static class ExceptionInfo { - private int end; - private int handler; - private int start; - private Type type; - - private ExceptionInfo(int start, int end, int handler, Type type) { - this.start = start; - this.end = end; - this.handler = handler; - this.type = type; - } - } - - /** - * Performs data-flow analysis on a method and returns an array, indexed by - * instruction position, containing the starting frame state of all reachable - * instructions. Non-reachable code, and illegal code offsets are represented - * as a null in the frame state array. This can be used to detect dead code. - * - * If the method does not contain code (it is either native or abstract), null - * is returned. - * - * @param clazz the declaring class of the method - * @param method the method to analyze - * @return an array, indexed by instruction position, of the starting frame state, - * or null if this method doesn't have code - * @throws BadBytecode if the bytecode does not comply with the JVM specification - */ - public Frame[] analyze(CtClass clazz, MethodInfo method) throws BadBytecode { - this.clazz = clazz; - CodeAttribute codeAttribute = method.getCodeAttribute(); - // Native or Abstract - if (codeAttribute == null) - return null; - - int maxLocals = codeAttribute.getMaxLocals(); - int maxStack = codeAttribute.getMaxStack(); - int codeLength = codeAttribute.getCodeLength(); - - CodeIterator iter = codeAttribute.iterator(); - IntQueue queue = new IntQueue(); - - exceptions = buildExceptionInfo(method); - subroutines = scanner.scan(method); - - Executor executor = new Executor(clazz.getClassPool(), method.getConstPool()); - frames = new Frame[codeLength]; - frames[iter.lookAhead()] = firstFrame(method, maxLocals, maxStack); - queue.add(iter.next()); - while (!queue.isEmpty()) { - analyzeNextEntry(method, iter, queue, executor); - } - - return frames; - } - - /** - * Performs data-flow analysis on a method and returns an array, indexed by - * instruction position, containing the starting frame state of all reachable - * instructions. Non-reachable code, and illegal code offsets are represented - * as a null in the frame state array. This can be used to detect dead code. - * - * If the method does not contain code (it is either native or abstract), null - * is returned. - * - * @param method the method to analyze - * @return an array, indexed by instruction position, of the starting frame state, - * or null if this method doesn't have code - * @throws BadBytecode if the bytecode does not comply with the JVM specification - */ - public Frame[] analyze(CtMethod method) throws BadBytecode { - return analyze(method.getDeclaringClass(), method.getMethodInfo2()); - } - - private void analyzeNextEntry(MethodInfo method, CodeIterator iter, - IntQueue queue, Executor executor) throws BadBytecode { - int pos = queue.take(); - iter.move(pos); - iter.next(); - - Frame frame = frames[pos].copy(); - Subroutine subroutine = subroutines[pos]; - - try { - executor.execute(method, pos, iter, frame, subroutine); - } catch (RuntimeException e) { - throw new BadBytecode(e.getMessage() + "[pos = " + pos + "]", e); - } - - int opcode = iter.byteAt(pos); - - if (opcode == TABLESWITCH) { - mergeTableSwitch(queue, pos, iter, frame); - } else if (opcode == LOOKUPSWITCH) { - mergeLookupSwitch(queue, pos, iter, frame); - } else if (opcode == RET) { - mergeRet(queue, iter, pos, frame, subroutine); - } else if (Util.isJumpInstruction(opcode)) { - int target = Util.getJumpTarget(pos, iter); - - if (Util.isJsr(opcode)) { - // Merge the state before the jsr into the next instruction - mergeJsr(queue, frames[pos], subroutines[target], pos, lookAhead(iter, pos)); - } else if (! Util.isGoto(opcode)) { - merge(queue, frame, lookAhead(iter, pos)); - } - - merge(queue, frame, target); - } else if (opcode != ATHROW && ! Util.isReturn(opcode)) { - // Can advance to next instruction - merge(queue, frame, lookAhead(iter, pos)); - } - - // Merge all exceptions that are reachable from this instruction. - // The redundancy is intentional, since the state must be based - // on the current instruction frame. - mergeExceptionHandlers(queue, method, pos, frame); - } - - private ExceptionInfo[] buildExceptionInfo(MethodInfo method) { - ConstPool constPool = method.getConstPool(); - ClassPool classes = clazz.getClassPool(); - - ExceptionTable table = method.getCodeAttribute().getExceptionTable(); - ExceptionInfo[] exceptions = new ExceptionInfo[table.size()]; - for (int i = 0; i < table.size(); i++) { - int index = table.catchType(i); - Type type; - try { - type = index == 0 ? Type.THROWABLE : Type.get(classes.get(constPool.getClassInfo(index))); - } catch (NotFoundException e) { - throw new IllegalStateException(e.getMessage()); - } - - exceptions[i] = new ExceptionInfo(table.startPc(i), table.endPc(i), table.handlerPc(i), type); - } - - return exceptions; - } - - private Frame firstFrame(MethodInfo method, int maxLocals, int maxStack) { - int pos = 0; - - Frame first = new Frame(maxLocals, maxStack); - if ((method.getAccessFlags() & AccessFlag.STATIC) == 0) { - first.setLocal(pos++, Type.get(clazz)); - } - - CtClass[] parameters; - try { - parameters = Descriptor.getParameterTypes(method.getDescriptor(), clazz.getClassPool()); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - - for (int i = 0; i < parameters.length; i++) { - Type type = zeroExtend(Type.get(parameters[i])); - first.setLocal(pos++, type); - if (type.getSize() == 2) - first.setLocal(pos++, Type.TOP); - } - - return first; - } - - private int getNext(CodeIterator iter, int of, int restore) throws BadBytecode { - iter.move(of); - iter.next(); - int next = iter.lookAhead(); - iter.move(restore); - iter.next(); - - return next; - } - - private int lookAhead(CodeIterator iter, int pos) throws BadBytecode { - if (! iter.hasNext()) - throw new BadBytecode("Execution falls off end! [pos = " + pos + "]"); - - return iter.lookAhead(); - } - - - private void merge(IntQueue queue, Frame frame, int target) { - Frame old = frames[target]; - boolean changed; - - if (old == null) { - frames[target] = frame.copy(); - changed = true; - } else { - changed = old.merge(frame); - } - - if (changed) { - queue.add(target); - } - } - - private void mergeExceptionHandlers(IntQueue queue, MethodInfo method, int pos, Frame frame) { - for (int i = 0; i < exceptions.length; i++) { - ExceptionInfo exception = exceptions[i]; - - // Start is inclusive, while end is exclusive! - if (pos >= exception.start && pos < exception.end) { - Frame newFrame = frame.copy(); - newFrame.clearStack(); - newFrame.push(exception.type); - merge(queue, newFrame, exception.handler); - } - } - } - - private void mergeJsr(IntQueue queue, Frame frame, Subroutine sub, int pos, int next) throws BadBytecode { - if (sub == null) - throw new BadBytecode("No subroutine at jsr target! [pos = " + pos + "]"); - - Frame old = frames[next]; - boolean changed = false; - - if (old == null) { - old = frames[next] = frame.copy(); - changed = true; - } else { - for (int i = 0; i < frame.localsLength(); i++) { - // Skip everything accessed by a subroutine, mergeRet must handle this - if (!sub.isAccessed(i)) { - Type oldType = old.getLocal(i); - Type newType = frame.getLocal(i); - if (oldType == null) { - old.setLocal(i, newType); - changed = true; - continue; - } - - newType = oldType.merge(newType); - // Always set the type, in case a multi-type switched to a standard type. - old.setLocal(i, newType); - if (!newType.equals(oldType) || newType.popChanged()) - changed = true; - } - } - } - - if (! old.isJsrMerged()) { - old.setJsrMerged(true); - changed = true; - } - - if (changed && old.isRetMerged()) - queue.add(next); - - } - - private void mergeLookupSwitch(IntQueue queue, int pos, CodeIterator iter, Frame frame) throws BadBytecode { - int index = (pos & ~3) + 4; - // default - merge(queue, frame, pos + iter.s32bitAt(index)); - int npairs = iter.s32bitAt(index += 4); - int end = npairs * 8 + (index += 4); - - // skip "match" - for (index += 4; index < end; index += 8) { - int target = iter.s32bitAt(index) + pos; - merge(queue, frame, target); - } - } - - private void mergeRet(IntQueue queue, CodeIterator iter, int pos, Frame frame, Subroutine subroutine) throws BadBytecode { - if (subroutine == null) - throw new BadBytecode("Ret on no subroutine! [pos = " + pos + "]"); - - Iterator callerIter = subroutine.callers().iterator(); - while (callerIter.hasNext()) { - int caller = ((Integer) callerIter.next()).intValue(); - int returnLoc = getNext(iter, caller, pos); - boolean changed = false; - - Frame old = frames[returnLoc]; - if (old == null) { - old = frames[returnLoc] = frame.copyStack(); - changed = true; - } else { - changed = old.mergeStack(frame); - } - - for (Iterator i = subroutine.accessed().iterator(); i.hasNext(); ) { - int index = ((Integer)i.next()).intValue(); - Type oldType = old.getLocal(index); - Type newType = frame.getLocal(index); - if (oldType != newType) { - old.setLocal(index, newType); - changed = true; - } - } - - if (! old.isRetMerged()) { - old.setRetMerged(true); - changed = true; - } - - if (changed && old.isJsrMerged()) - queue.add(returnLoc); - } - } - - - private void mergeTableSwitch(IntQueue queue, int pos, CodeIterator iter, Frame frame) throws BadBytecode { - // Skip 4 byte alignment padding - int index = (pos & ~3) + 4; - // default - merge(queue, frame, pos + iter.s32bitAt(index)); - int low = iter.s32bitAt(index += 4); - int high = iter.s32bitAt(index += 4); - int end = (high - low + 1) * 4 + (index += 4); - - // Offset table - for (; index < end; index += 4) { - int target = iter.s32bitAt(index) + pos; - merge(queue, frame, target); - } - } - - private Type zeroExtend(Type type) { - if (type == Type.SHORT || type == Type.BYTE || type == Type.CHAR || type == Type.BOOLEAN) - return Type.INTEGER; - - return type; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java deleted file mode 100644 index d37d84c..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.analysis; - -import java.util.ArrayList; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; -import com.wenshuo.agent.javassist.bytecode.stackmap.BasicBlock; - -/** - * Represents the control flow graph of a given method. - * - *

To obtain the control flow graph, do the following:

- * - *
CtMethod m = ...
- * ControlFlow cf = new ControlFlow(m);
- * Block[] blocks = cf.basicBlocks();
- * 
- * - *

blocks is an array of basic blocks in - * that method body.

- * - * @see javassist.CtMethod - * @see Block - * @see Frame - * @see Analyzer - * @author Shigeru Chiba - * @since 3.16 - */ -public class ControlFlow { - private CtClass clazz; - private MethodInfo methodInfo; - private Block[] basicBlocks; - private Frame[] frames; - - /** - * Constructs a control-flow analyzer for the given method. - */ - public ControlFlow(CtMethod method) throws BadBytecode { - this(method.getDeclaringClass(), method.getMethodInfo2()); - } - - /** - * Constructs a control-flow analyzer. - */ - public ControlFlow(CtClass ctclazz, MethodInfo minfo) throws BadBytecode { - clazz = ctclazz; - methodInfo = minfo; - frames = null; - basicBlocks = (Block[])new BasicBlock.Maker() { - protected BasicBlock makeBlock(int pos) { - return new Block(pos, methodInfo); - } - protected BasicBlock[] makeArray(int size) { - return new Block[size]; - } - }.make(minfo); - int size = basicBlocks.length; - int[] counters = new int[size]; - for (int i = 0; i < size; i++) { - Block b = basicBlocks[i]; - b.index = i; - b.entrances = new Block[b.incomings()]; - counters[i] = 0; - } - - for (int i = 0; i < size; i++) { - Block b = basicBlocks[i]; - for (int k = 0; k < b.exits(); k++) { - Block e = b.exit(k); - e.entrances[counters[e.index]++] = b; - } - - ControlFlow.Catcher[] catchers = b.catchers(); - for (int k = 0; k < catchers.length; k++) { - Block catchBlock = catchers[k].node; - catchBlock.entrances[counters[catchBlock.index]++] = b; - } - } - } - - /** - * Returns all the basic blocks in the method body. - */ - public Block[] basicBlocks() { - return basicBlocks; - } - - /** - * Returns the types of the local variables and stack frame entries - * available at the given position. If the byte at the position is - * not the first byte of an instruction, then this method returns - * null. - * - * @param pos the position. - */ - public Frame frameAt(int pos) throws BadBytecode { - if (frames == null) - frames = new Analyzer().analyze(clazz, methodInfo); - - return frames[pos]; - } - - /** - * Constructs a dominator tree. This method returns an array of - * the tree nodes. The first element of the array is the root - * of the tree. - * - *

The order of the elements is the same as that - * of the elements in the Block array returned - * by the basicBlocks - * method. If a Block object is at the i-th position - * in the Block array, then - * the Node object referring to that - * Block object is at the i-th position in the - * array returned by this method. - * For every array element node, its index in the - * array is equivalent to node.block().index(). - * - * @return an array of the tree nodes, or null if the method is abstract. - * @see Node#block() - * @see Block#index() - */ - public Node[] dominatorTree() { - int size = basicBlocks.length; - if (size == 0) - return null; - - Node[] nodes = new Node[size]; - boolean[] visited = new boolean[size]; - int[] distance = new int[size]; - for (int i = 0; i < size; i++) { - nodes[i] = new Node(basicBlocks[i]); - visited[i] = false; - } - - Access access = new Access(nodes) { - BasicBlock[] exits(Node n) { return n.block.getExit(); } - BasicBlock[] entrances(Node n) { return n.block.entrances; } - }; - nodes[0].makeDepth1stTree(null, visited, 0, distance, access); - do { - for (int i = 0; i < size; i++) - visited[i] = false; - } while (nodes[0].makeDominatorTree(visited, distance, access)); - Node.setChildren(nodes); - return nodes; - } - - /** - * Constructs a post dominator tree. This method returns an array of - * the tree nodes. Note that the tree has multiple roots. - * The parent of the root nodes is null. - * - *

The order of the elements is the same as that - * of the elements in the Block array returned - * by the basicBlocks - * method. If a Block object is at the i-th position - * in the Block array, then - * the Node object referring to that - * Block object is at the i-th position in the - * array returned by this method. - * For every array element node, its index in the - * array is equivalent to node.block().index(). - * - * @return an array of the tree nodes, or null if the method is abstract. - * @see Node#block() - * @see Block#index() - */ - public Node[] postDominatorTree() { - int size = basicBlocks.length; - if (size == 0) - return null; - - Node[] nodes = new Node[size]; - boolean[] visited = new boolean[size]; - int[] distance = new int[size]; - for (int i = 0; i < size; i++) { - nodes[i] = new Node(basicBlocks[i]); - visited[i] = false; - } - - Access access = new Access(nodes) { - BasicBlock[] exits(Node n) { return n.block.entrances; } - BasicBlock[] entrances(Node n) { return n.block.getExit(); } - }; - - int counter = 0; - for (int i = 0; i < size; i++) - if (nodes[i].block.exits() == 0) - counter = nodes[i].makeDepth1stTree(null, visited, counter, distance, access); - - boolean changed; - do { - for (int i = 0; i < size; i++) - visited[i] = false; - - changed = false; - for (int i = 0; i < size; i++) - if (nodes[i].block.exits() == 0) - if (nodes[i].makeDominatorTree(visited, distance, access)) - changed = true; - } while (changed); - - Node.setChildren(nodes); - return nodes; - } - - /** - * Basic block. - * It is a sequence of contiguous instructions that do not contain - * jump/branch instructions except the last one. - * Since Java6 or later does not allow JSR, - * we deal with JSR as a non-branch instruction. - */ - public static class Block extends BasicBlock { - /** - * A field that can be freely used for storing extra data. - * A client program of this control-flow analyzer can append - * an additional attribute to a Block object. - * The Javassist library never accesses this field. - */ - public Object clientData = null; - - int index; - MethodInfo method; - Block[] entrances; - - Block(int pos, MethodInfo minfo) { - super(pos); - method = minfo; - } - - protected void toString2(StringBuffer sbuf) { - super.toString2(sbuf); - sbuf.append(", incoming{"); - for (int i = 0; i < entrances.length; i++) - sbuf.append(entrances[i].position).append(", "); - - sbuf.append("}"); - } - - BasicBlock[] getExit() { return exit; } - - /** - * Returns the position of this block in the array of - * basic blocks that the basicBlocks method - * returns. - * - * @see #basicBlocks() - */ - public int index() { return index; } - - /** - * Returns the position of the first instruction - * in this block. - */ - public int position() { return position; } - - /** - * Returns the length of this block. - */ - public int length() { return length; } - - /** - * Returns the number of the control paths entering this block. - */ - public int incomings() { return incoming; } - - /** - * Returns the block that the control may jump into this block from. - */ - public Block incoming(int n) { - return entrances[n]; - } - - /** - * Return the number of the blocks that may be executed - * after this block. - */ - public int exits() { return exit == null ? 0 : exit.length; } - - /** - * Returns the n-th block that may be executed after this - * block. - * - * @param n an index in the array of exit blocks. - */ - public Block exit(int n) { return (Block)exit[n]; } - - /** - * Returns catch clauses that will catch an exception thrown - * in this block. - */ - public Catcher[] catchers() { - ArrayList catchers = new ArrayList(); - BasicBlock.Catch c = toCatch; - while (c != null) { - catchers.add(new Catcher(c)); - c = c.next; - } - - return (Catcher[])catchers.toArray(new Catcher[catchers.size()]); - } - } - - static abstract class Access { - Node[] all; - Access(Node[] nodes) { all = nodes; } - Node node(BasicBlock b) { return all[((Block)b).index]; } - abstract BasicBlock[] exits(Node n); - abstract BasicBlock[] entrances(Node n); - } - - /** - * A node of (post) dominator trees. - */ - public static class Node { - private Block block; - private Node parent; - private Node[] children; - - Node(Block b) { - block = b; - parent = null; - } - - /** - * Returns a String representation. - */ - public String toString() { - StringBuffer sbuf = new StringBuffer(); - sbuf.append("Node[pos=").append(block().position()); - sbuf.append(", parent="); - sbuf.append(parent == null ? "*" : Integer.toString(parent.block().position())); - sbuf.append(", children{"); - for (int i = 0; i < children.length; i++) - sbuf.append(children[i].block().position()).append(", "); - - sbuf.append("}]"); - return sbuf.toString(); - } - - /** - * Returns the basic block indicated by this node. - */ - public Block block() { return block; } - - /** - * Returns the parent of this node. - */ - public Node parent() { return parent; } - - /** - * Returns the number of the children of this node. - */ - public int children() { return children.length; } - - /** - * Returns the n-th child of this node. - * - * @param n an index in the array of children. - */ - public Node child(int n) { return children[n]; } - - /* - * After executing this method, distance[] represents the post order of the tree nodes. - * It also represents distances from the root; a bigger number represents a shorter - * distance. parent is set to its parent in the depth first spanning tree. - */ - int makeDepth1stTree(Node caller, boolean[] visited, int counter, int[] distance, Access access) { - int index = block.index; - if (visited[index]) - return counter; - - visited[index] = true; - parent = caller; - BasicBlock[] exits = access.exits(this); - if (exits != null) - for (int i = 0; i < exits.length; i++) { - Node n = access.node(exits[i]); - counter = n.makeDepth1stTree(this, visited, counter, distance, access); - } - - distance[index] = counter++; - return counter; - } - - boolean makeDominatorTree(boolean[] visited, int[] distance, Access access) { - int index = block.index; - if (visited[index]) - return false; - - visited[index] = true; - boolean changed = false; - BasicBlock[] exits = access.exits(this); - if (exits != null) - for (int i = 0; i < exits.length; i++) { - Node n = access.node(exits[i]); - if (n.makeDominatorTree(visited, distance, access)) - changed = true; - } - - BasicBlock[] entrances = access.entrances(this); - if (entrances != null) - for (int i = 0; i < entrances.length; i++) { - if (parent != null) { - Node n = getAncestor(parent, access.node(entrances[i]), distance); - if (n != parent) { - parent = n; - changed = true; - } - } - } - - return changed; - } - - private static Node getAncestor(Node n1, Node n2, int[] distance) { - while (n1 != n2) { - if (distance[n1.block.index] < distance[n2.block.index]) - n1 = n1.parent; - else - n2 = n2.parent; - - if (n1 == null || n2 == null) - return null; - } - - return n1; - } - - private static void setChildren(Node[] all) { - int size = all.length; - int[] nchildren = new int[size]; - for (int i = 0; i < size; i++) - nchildren[i] = 0; - - for (int i = 0; i < size; i++) { - Node p = all[i].parent; - if (p != null) - nchildren[p.block.index]++; - } - - for (int i = 0; i < size; i++) - all[i].children = new Node[nchildren[i]]; - - for (int i = 0; i < size; i++) - nchildren[i] = 0; - - for (int i = 0; i < size; i++) { - Node n = all[i]; - Node p = n.parent; - if (p != null) - p.children[nchildren[p.block.index]++] = n; - } - } - } - - /** - * Represents a catch clause. - */ - public static class Catcher { - private Block node; - private int typeIndex; - - Catcher(BasicBlock.Catch c) { - node = (Block)c.body; - typeIndex = c.typeIndex; - } - - /** - * Returns the first block of the catch clause. - */ - public Block block() { return node; } - - /** - * Returns the name of the exception type that - * this catch clause catches. - */ - public String type() { - if (typeIndex == 0) - return "java.lang.Throwable"; - else - return node.method.getConstPool().getClassInfo(typeIndex); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java deleted file mode 100644 index 76e7e82..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java +++ /dev/null @@ -1,1047 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; -import com.wenshuo.agent.javassist.bytecode.Opcode; - -/** - * Executor is responsible for modeling the effects of a JVM instruction on a frame. - * - * @author Jason T. Greene - */ -public class Executor implements Opcode { - private final ConstPool constPool; - private final ClassPool classPool; - private final Type STRING_TYPE; - private final Type CLASS_TYPE; - private final Type THROWABLE_TYPE; - private int lastPos; - - public Executor(ClassPool classPool, ConstPool constPool) { - this.constPool = constPool; - this.classPool = classPool; - - try { - STRING_TYPE = getType("java.lang.String"); - CLASS_TYPE = getType("java.lang.Class"); - THROWABLE_TYPE = getType("java.lang.Throwable"); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - - /** - * Execute the instruction, modeling the effects on the specified frame and subroutine. - * If a subroutine is passed, the access flags will be modified if this instruction accesses - * the local variable table. - * - * @param method the method containing the instruction - * @param pos the position of the instruction in the method - * @param iter the code iterator used to find the instruction - * @param frame the frame to modify to represent the result of the instruction - * @param subroutine the optional subroutine this instruction belongs to. - * @throws BadBytecode if the bytecode violates the jvm spec - */ - public void execute(MethodInfo method, int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode { - this.lastPos = pos; - int opcode = iter.byteAt(pos); - - - // Declared opcode in order - switch (opcode) { - case NOP: - break; - case ACONST_NULL: - frame.push(Type.UNINIT); - break; - case ICONST_M1: - case ICONST_0: - case ICONST_1: - case ICONST_2: - case ICONST_3: - case ICONST_4: - case ICONST_5: - frame.push(Type.INTEGER); - break; - case LCONST_0: - case LCONST_1: - frame.push(Type.LONG); - frame.push(Type.TOP); - break; - case FCONST_0: - case FCONST_1: - case FCONST_2: - frame.push(Type.FLOAT); - break; - case DCONST_0: - case DCONST_1: - frame.push(Type.DOUBLE); - frame.push(Type.TOP); - break; - case BIPUSH: - case SIPUSH: - frame.push(Type.INTEGER); - break; - case LDC: - evalLDC(iter.byteAt(pos + 1), frame); - break; - case LDC_W : - case LDC2_W : - evalLDC(iter.u16bitAt(pos + 1), frame); - break; - case ILOAD: - evalLoad(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine); - break; - case LLOAD: - evalLoad(Type.LONG, iter.byteAt(pos + 1), frame, subroutine); - break; - case FLOAD: - evalLoad(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine); - break; - case DLOAD: - evalLoad(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine); - break; - case ALOAD: - evalLoad(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine); - break; - case ILOAD_0: - case ILOAD_1: - case ILOAD_2: - case ILOAD_3: - evalLoad(Type.INTEGER, opcode - ILOAD_0, frame, subroutine); - break; - case LLOAD_0: - case LLOAD_1: - case LLOAD_2: - case LLOAD_3: - evalLoad(Type.LONG, opcode - LLOAD_0, frame, subroutine); - break; - case FLOAD_0: - case FLOAD_1: - case FLOAD_2: - case FLOAD_3: - evalLoad(Type.FLOAT, opcode - FLOAD_0, frame, subroutine); - break; - case DLOAD_0: - case DLOAD_1: - case DLOAD_2: - case DLOAD_3: - evalLoad(Type.DOUBLE, opcode - DLOAD_0, frame, subroutine); - break; - case ALOAD_0: - case ALOAD_1: - case ALOAD_2: - case ALOAD_3: - evalLoad(Type.OBJECT, opcode - ALOAD_0, frame, subroutine); - break; - case IALOAD: - evalArrayLoad(Type.INTEGER, frame); - break; - case LALOAD: - evalArrayLoad(Type.LONG, frame); - break; - case FALOAD: - evalArrayLoad(Type.FLOAT, frame); - break; - case DALOAD: - evalArrayLoad(Type.DOUBLE, frame); - break; - case AALOAD: - evalArrayLoad(Type.OBJECT, frame); - break; - case BALOAD: - case CALOAD: - case SALOAD: - evalArrayLoad(Type.INTEGER, frame); - break; - case ISTORE: - evalStore(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine); - break; - case LSTORE: - evalStore(Type.LONG, iter.byteAt(pos + 1), frame, subroutine); - break; - case FSTORE: - evalStore(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine); - break; - case DSTORE: - evalStore(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine); - break; - case ASTORE: - evalStore(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine); - break; - case ISTORE_0: - case ISTORE_1: - case ISTORE_2: - case ISTORE_3: - evalStore(Type.INTEGER, opcode - ISTORE_0, frame, subroutine); - break; - case LSTORE_0: - case LSTORE_1: - case LSTORE_2: - case LSTORE_3: - evalStore(Type.LONG, opcode - LSTORE_0, frame, subroutine); - break; - case FSTORE_0: - case FSTORE_1: - case FSTORE_2: - case FSTORE_3: - evalStore(Type.FLOAT, opcode - FSTORE_0, frame, subroutine); - break; - case DSTORE_0: - case DSTORE_1: - case DSTORE_2: - case DSTORE_3: - evalStore(Type.DOUBLE, opcode - DSTORE_0, frame, subroutine); - break; - case ASTORE_0: - case ASTORE_1: - case ASTORE_2: - case ASTORE_3: - evalStore(Type.OBJECT, opcode - ASTORE_0, frame, subroutine); - break; - case IASTORE: - evalArrayStore(Type.INTEGER, frame); - break; - case LASTORE: - evalArrayStore(Type.LONG, frame); - break; - case FASTORE: - evalArrayStore(Type.FLOAT, frame); - break; - case DASTORE: - evalArrayStore(Type.DOUBLE, frame); - break; - case AASTORE: - evalArrayStore(Type.OBJECT, frame); - break; - case BASTORE: - case CASTORE: - case SASTORE: - evalArrayStore(Type.INTEGER, frame); - break; - case POP: - if (frame.pop() == Type.TOP) - throw new BadBytecode("POP can not be used with a category 2 value, pos = " + pos); - break; - case POP2: - frame.pop(); - frame.pop(); - break; - case DUP: { - Type type = frame.peek(); - if (type == Type.TOP) - throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos); - - frame.push(frame.peek()); - break; - } - case DUP_X1: - case DUP_X2: { - Type type = frame.peek(); - if (type == Type.TOP) - throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos); - int end = frame.getTopIndex(); - int insert = end - (opcode - DUP_X1) - 1; - frame.push(type); - - while (end > insert) { - frame.setStack(end, frame.getStack(end - 1)); - end--; - } - frame.setStack(insert, type); - break; - } - case DUP2: - frame.push(frame.getStack(frame.getTopIndex() - 1)); - frame.push(frame.getStack(frame.getTopIndex() - 1)); - break; - case DUP2_X1: - case DUP2_X2: { - int end = frame.getTopIndex(); - int insert = end - (opcode - DUP2_X1) - 1; - Type type1 = frame.getStack(frame.getTopIndex() - 1); - Type type2 = frame.peek(); - frame.push(type1); - frame.push(type2); - while (end > insert) { - frame.setStack(end, frame.getStack(end - 2)); - end--; - } - frame.setStack(insert, type2); - frame.setStack(insert - 1, type1); - break; - } - case SWAP: { - Type type1 = frame.pop(); - Type type2 = frame.pop(); - if (type1.getSize() == 2 || type2.getSize() == 2) - throw new BadBytecode("Swap can not be used with category 2 values, pos = " + pos); - frame.push(type1); - frame.push(type2); - break; - } - - // Math - case IADD: - evalBinaryMath(Type.INTEGER, frame); - break; - case LADD: - evalBinaryMath(Type.LONG, frame); - break; - case FADD: - evalBinaryMath(Type.FLOAT, frame); - break; - case DADD: - evalBinaryMath(Type.DOUBLE, frame); - break; - case ISUB: - evalBinaryMath(Type.INTEGER, frame); - break; - case LSUB: - evalBinaryMath(Type.LONG, frame); - break; - case FSUB: - evalBinaryMath(Type.FLOAT, frame); - break; - case DSUB: - evalBinaryMath(Type.DOUBLE, frame); - break; - case IMUL: - evalBinaryMath(Type.INTEGER, frame); - break; - case LMUL: - evalBinaryMath(Type.LONG, frame); - break; - case FMUL: - evalBinaryMath(Type.FLOAT, frame); - break; - case DMUL: - evalBinaryMath(Type.DOUBLE, frame); - break; - case IDIV: - evalBinaryMath(Type.INTEGER, frame); - break; - case LDIV: - evalBinaryMath(Type.LONG, frame); - break; - case FDIV: - evalBinaryMath(Type.FLOAT, frame); - break; - case DDIV: - evalBinaryMath(Type.DOUBLE, frame); - break; - case IREM: - evalBinaryMath(Type.INTEGER, frame); - break; - case LREM: - evalBinaryMath(Type.LONG, frame); - break; - case FREM: - evalBinaryMath(Type.FLOAT, frame); - break; - case DREM: - evalBinaryMath(Type.DOUBLE, frame); - break; - - // Unary - case INEG: - verifyAssignable(Type.INTEGER, simplePeek(frame)); - break; - case LNEG: - verifyAssignable(Type.LONG, simplePeek(frame)); - break; - case FNEG: - verifyAssignable(Type.FLOAT, simplePeek(frame)); - break; - case DNEG: - verifyAssignable(Type.DOUBLE, simplePeek(frame)); - break; - - // Shifts - case ISHL: - evalShift(Type.INTEGER, frame); - break; - case LSHL: - evalShift(Type.LONG, frame); - break; - case ISHR: - evalShift(Type.INTEGER, frame); - break; - case LSHR: - evalShift(Type.LONG, frame); - break; - case IUSHR: - evalShift(Type.INTEGER,frame); - break; - case LUSHR: - evalShift(Type.LONG, frame); - break; - - // Bitwise Math - case IAND: - evalBinaryMath(Type.INTEGER, frame); - break; - case LAND: - evalBinaryMath(Type.LONG, frame); - break; - case IOR: - evalBinaryMath(Type.INTEGER, frame); - break; - case LOR: - evalBinaryMath(Type.LONG, frame); - break; - case IXOR: - evalBinaryMath(Type.INTEGER, frame); - break; - case LXOR: - evalBinaryMath(Type.LONG, frame); - break; - - case IINC: { - int index = iter.byteAt(pos + 1); - verifyAssignable(Type.INTEGER, frame.getLocal(index)); - access(index, Type.INTEGER, subroutine); - break; - } - - // Conversion - case I2L: - verifyAssignable(Type.INTEGER, simplePop(frame)); - simplePush(Type.LONG, frame); - break; - case I2F: - verifyAssignable(Type.INTEGER, simplePop(frame)); - simplePush(Type.FLOAT, frame); - break; - case I2D: - verifyAssignable(Type.INTEGER, simplePop(frame)); - simplePush(Type.DOUBLE, frame); - break; - case L2I: - verifyAssignable(Type.LONG, simplePop(frame)); - simplePush(Type.INTEGER, frame); - break; - case L2F: - verifyAssignable(Type.LONG, simplePop(frame)); - simplePush(Type.FLOAT, frame); - break; - case L2D: - verifyAssignable(Type.LONG, simplePop(frame)); - simplePush(Type.DOUBLE, frame); - break; - case F2I: - verifyAssignable(Type.FLOAT, simplePop(frame)); - simplePush(Type.INTEGER, frame); - break; - case F2L: - verifyAssignable(Type.FLOAT, simplePop(frame)); - simplePush(Type.LONG, frame); - break; - case F2D: - verifyAssignable(Type.FLOAT, simplePop(frame)); - simplePush(Type.DOUBLE, frame); - break; - case D2I: - verifyAssignable(Type.DOUBLE, simplePop(frame)); - simplePush(Type.INTEGER, frame); - break; - case D2L: - verifyAssignable(Type.DOUBLE, simplePop(frame)); - simplePush(Type.LONG, frame); - break; - case D2F: - verifyAssignable(Type.DOUBLE, simplePop(frame)); - simplePush(Type.FLOAT, frame); - break; - case I2B: - case I2C: - case I2S: - verifyAssignable(Type.INTEGER, frame.peek()); - break; - case LCMP: - verifyAssignable(Type.LONG, simplePop(frame)); - verifyAssignable(Type.LONG, simplePop(frame)); - frame.push(Type.INTEGER); - break; - case FCMPL: - case FCMPG: - verifyAssignable(Type.FLOAT, simplePop(frame)); - verifyAssignable(Type.FLOAT, simplePop(frame)); - frame.push(Type.INTEGER); - break; - case DCMPL: - case DCMPG: - verifyAssignable(Type.DOUBLE, simplePop(frame)); - verifyAssignable(Type.DOUBLE, simplePop(frame)); - frame.push(Type.INTEGER); - break; - - // Control flow - case IFEQ: - case IFNE: - case IFLT: - case IFGE: - case IFGT: - case IFLE: - verifyAssignable(Type.INTEGER, simplePop(frame)); - break; - case IF_ICMPEQ: - case IF_ICMPNE: - case IF_ICMPLT: - case IF_ICMPGE: - case IF_ICMPGT: - case IF_ICMPLE: - verifyAssignable(Type.INTEGER, simplePop(frame)); - verifyAssignable(Type.INTEGER, simplePop(frame)); - break; - case IF_ACMPEQ: - case IF_ACMPNE: - verifyAssignable(Type.OBJECT, simplePop(frame)); - verifyAssignable(Type.OBJECT, simplePop(frame)); - break; - case GOTO: - break; - case JSR: - frame.push(Type.RETURN_ADDRESS); - break; - case RET: - verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(iter.byteAt(pos + 1))); - break; - case TABLESWITCH: - case LOOKUPSWITCH: - case IRETURN: - verifyAssignable(Type.INTEGER, simplePop(frame)); - break; - case LRETURN: - verifyAssignable(Type.LONG, simplePop(frame)); - break; - case FRETURN: - verifyAssignable(Type.FLOAT, simplePop(frame)); - break; - case DRETURN: - verifyAssignable(Type.DOUBLE, simplePop(frame)); - break; - case ARETURN: - try { - CtClass returnType = Descriptor.getReturnType(method.getDescriptor(), classPool); - verifyAssignable(Type.get(returnType), simplePop(frame)); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - break; - case RETURN: - break; - case GETSTATIC: - evalGetField(opcode, iter.u16bitAt(pos + 1), frame); - break; - case PUTSTATIC: - evalPutField(opcode, iter.u16bitAt(pos + 1), frame); - break; - case GETFIELD: - evalGetField(opcode, iter.u16bitAt(pos + 1), frame); - break; - case PUTFIELD: - evalPutField(opcode, iter.u16bitAt(pos + 1), frame); - break; - case INVOKEVIRTUAL: - case INVOKESPECIAL: - case INVOKESTATIC: - evalInvokeMethod(opcode, iter.u16bitAt(pos + 1), frame); - break; - case INVOKEINTERFACE: - evalInvokeIntfMethod(opcode, iter.u16bitAt(pos + 1), frame); - break; - case INVOKEDYNAMIC: - evalInvokeDynamic(opcode, iter.u16bitAt(pos + 1), frame); - break; - case NEW: - frame.push(resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1)))); - break; - case NEWARRAY: - evalNewArray(pos, iter, frame); - break; - case ANEWARRAY: - evalNewObjectArray(pos, iter, frame); - break; - case ARRAYLENGTH: { - Type array = simplePop(frame); - if (! array.isArray() && array != Type.UNINIT) - throw new BadBytecode("Array length passed a non-array [pos = " + pos + "]: " + array); - frame.push(Type.INTEGER); - break; - } - case ATHROW: - verifyAssignable(THROWABLE_TYPE, simplePop(frame)); - break; - case CHECKCAST: - verifyAssignable(Type.OBJECT, simplePop(frame)); - frame.push(typeFromDesc(constPool.getClassInfoByDescriptor(iter.u16bitAt(pos + 1)))); - break; - case INSTANCEOF: - verifyAssignable(Type.OBJECT, simplePop(frame)); - frame.push(Type.INTEGER); - break; - case MONITORENTER: - case MONITOREXIT: - verifyAssignable(Type.OBJECT, simplePop(frame)); - break; - case WIDE: - evalWide(pos, iter, frame, subroutine); - break; - case MULTIANEWARRAY: - evalNewObjectArray(pos, iter, frame); - break; - case IFNULL: - case IFNONNULL: - verifyAssignable(Type.OBJECT, simplePop(frame)); - break; - case GOTO_W: - break; - case JSR_W: - frame.push(Type.RETURN_ADDRESS); - break; - } - } - - private Type zeroExtend(Type type) { - if (type == Type.SHORT || type == Type.BYTE || type == Type.CHAR || type == Type.BOOLEAN) - return Type.INTEGER; - - return type; - } - - private void evalArrayLoad(Type expectedComponent, Frame frame) throws BadBytecode { - Type index = frame.pop(); - Type array = frame.pop(); - - // Special case, an array defined by aconst_null - // TODO - we might need to be more inteligent about this - if (array == Type.UNINIT) { - verifyAssignable(Type.INTEGER, index); - if (expectedComponent == Type.OBJECT) { - simplePush(Type.UNINIT, frame); - } else { - simplePush(expectedComponent, frame); - } - return; - } - - Type component = array.getComponent(); - - if (component == null) - throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component); - - component = zeroExtend(component); - - verifyAssignable(expectedComponent, component); - verifyAssignable(Type.INTEGER, index); - simplePush(component, frame); - } - - private void evalArrayStore(Type expectedComponent, Frame frame) throws BadBytecode { - Type value = simplePop(frame); - Type index = frame.pop(); - Type array = frame.pop(); - - if (array == Type.UNINIT) { - verifyAssignable(Type.INTEGER, index); - return; - } - - Type component = array.getComponent(); - - if (component == null) - throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component); - - component = zeroExtend(component); - - verifyAssignable(expectedComponent, component); - verifyAssignable(Type.INTEGER, index); - - // This intentionally only checks for Object on aastore - // downconverting of an array (no casts) - // e.g. Object[] blah = new String[]; - // blah[2] = (Object) "test"; - // blah[3] = new Integer(); // compiler doesnt catch it (has legal bytecode), - // // but will throw arraystoreexception - if (expectedComponent == Type.OBJECT) { - verifyAssignable(expectedComponent, value); - } else { - verifyAssignable(component, value); - } - } - - private void evalBinaryMath(Type expected, Frame frame) throws BadBytecode { - Type value2 = simplePop(frame); - Type value1 = simplePop(frame); - - verifyAssignable(expected, value2); - verifyAssignable(expected, value1); - simplePush(value1, frame); - } - - private void evalGetField(int opcode, int index, Frame frame) throws BadBytecode { - String desc = constPool.getFieldrefType(index); - Type type = zeroExtend(typeFromDesc(desc)); - - if (opcode == GETFIELD) { - Type objectType = resolveClassInfo(constPool.getFieldrefClassName(index)); - verifyAssignable(objectType, simplePop(frame)); - } - - simplePush(type, frame); - } - - private void evalInvokeIntfMethod(int opcode, int index, Frame frame) throws BadBytecode { - String desc = constPool.getInterfaceMethodrefType(index); - Type[] types = paramTypesFromDesc(desc); - int i = types.length; - - while (i > 0) - verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); - - String classInfo = constPool.getInterfaceMethodrefClassName(index); - Type objectType = resolveClassInfo(classInfo); - verifyAssignable(objectType, simplePop(frame)); - - Type returnType = returnTypeFromDesc(desc); - if (returnType != Type.VOID) - simplePush(zeroExtend(returnType), frame); - } - - private void evalInvokeMethod(int opcode, int index, Frame frame) throws BadBytecode { - String desc = constPool.getMethodrefType(index); - Type[] types = paramTypesFromDesc(desc); - int i = types.length; - - while (i > 0) - verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); - - if (opcode != INVOKESTATIC) { - Type objectType = resolveClassInfo(constPool.getMethodrefClassName(index)); - verifyAssignable(objectType, simplePop(frame)); - } - - Type returnType = returnTypeFromDesc(desc); - if (returnType != Type.VOID) - simplePush(zeroExtend(returnType), frame); - } - - private void evalInvokeDynamic(int opcode, int index, Frame frame) throws BadBytecode { - String desc = constPool.getInvokeDynamicType(index); - Type[] types = paramTypesFromDesc(desc); - int i = types.length; - - while (i > 0) - verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); - - // simplePop(frame); // assume CosntPool#REF_invokeStatic - - Type returnType = returnTypeFromDesc(desc); - if (returnType != Type.VOID) - simplePush(zeroExtend(returnType), frame); - } - - private void evalLDC(int index, Frame frame) throws BadBytecode { - int tag = constPool.getTag(index); - Type type; - switch (tag) { - case ConstPool.CONST_String: - type = STRING_TYPE; - break; - case ConstPool.CONST_Integer: - type = Type.INTEGER; - break; - case ConstPool.CONST_Float: - type = Type.FLOAT; - break; - case ConstPool.CONST_Long: - type = Type.LONG; - break; - case ConstPool.CONST_Double: - type = Type.DOUBLE; - break; - case ConstPool.CONST_Class: - type = CLASS_TYPE; - break; - default: - throw new BadBytecode("bad LDC [pos = " + lastPos + "]: " + tag); - } - - simplePush(type, frame); - } - - private void evalLoad(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode { - Type type = frame.getLocal(index); - - verifyAssignable(expected, type); - - simplePush(type, frame); - access(index, type, subroutine); - } - - private void evalNewArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode { - verifyAssignable(Type.INTEGER, simplePop(frame)); - Type type = null; - int typeInfo = iter.byteAt(pos + 1); - switch (typeInfo) { - case T_BOOLEAN: - type = getType("boolean[]"); - break; - case T_CHAR: - type = getType("char[]"); - break; - case T_BYTE: - type = getType("byte[]"); - break; - case T_SHORT: - type = getType("short[]"); - break; - case T_INT: - type = getType("int[]"); - break; - case T_LONG: - type = getType("long[]"); - break; - case T_FLOAT: - type = getType("float[]"); - break; - case T_DOUBLE: - type = getType("double[]"); - break; - default: - throw new BadBytecode("Invalid array type [pos = " + pos + "]: " + typeInfo); - - } - - frame.push(type); - } - - private void evalNewObjectArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode { - // Convert to x[] format - Type type = resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1))); - String name = type.getCtClass().getName(); - int opcode = iter.byteAt(pos); - int dimensions; - - if (opcode == MULTIANEWARRAY) { - dimensions = iter.byteAt(pos + 3); - } else { - name = name + "[]"; - dimensions = 1; - } - - while (dimensions-- > 0) { - verifyAssignable(Type.INTEGER, simplePop(frame)); - } - - simplePush(getType(name), frame); - } - - private void evalPutField(int opcode, int index, Frame frame) throws BadBytecode { - String desc = constPool.getFieldrefType(index); - Type type = zeroExtend(typeFromDesc(desc)); - - verifyAssignable(type, simplePop(frame)); - - if (opcode == PUTFIELD) { - Type objectType = resolveClassInfo(constPool.getFieldrefClassName(index)); - verifyAssignable(objectType, simplePop(frame)); - } - } - - private void evalShift(Type expected, Frame frame) throws BadBytecode { - Type value2 = simplePop(frame); - Type value1 = simplePop(frame); - - verifyAssignable(Type.INTEGER, value2); - verifyAssignable(expected, value1); - simplePush(value1, frame); - } - - private void evalStore(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode { - Type type = simplePop(frame); - - // RETURN_ADDRESS is allowed by ASTORE - if (! (expected == Type.OBJECT && type == Type.RETURN_ADDRESS)) - verifyAssignable(expected, type); - simpleSetLocal(index, type, frame); - access(index, type, subroutine); - } - - private void evalWide(int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode { - int opcode = iter.byteAt(pos + 1); - int index = iter.u16bitAt(pos + 2); - switch (opcode) { - case ILOAD: - evalLoad(Type.INTEGER, index, frame, subroutine); - break; - case LLOAD: - evalLoad(Type.LONG, index, frame, subroutine); - break; - case FLOAD: - evalLoad(Type.FLOAT, index, frame, subroutine); - break; - case DLOAD: - evalLoad(Type.DOUBLE, index, frame, subroutine); - break; - case ALOAD: - evalLoad(Type.OBJECT, index, frame, subroutine); - break; - case ISTORE: - evalStore(Type.INTEGER, index, frame, subroutine); - break; - case LSTORE: - evalStore(Type.LONG, index, frame, subroutine); - break; - case FSTORE: - evalStore(Type.FLOAT, index, frame, subroutine); - break; - case DSTORE: - evalStore(Type.DOUBLE, index, frame, subroutine); - break; - case ASTORE: - evalStore(Type.OBJECT, index, frame, subroutine); - break; - case IINC: - verifyAssignable(Type.INTEGER, frame.getLocal(index)); - break; - case RET: - verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(index)); - break; - default: - throw new BadBytecode("Invalid WIDE operand [pos = " + pos + "]: " + opcode); - } - - } - - private Type getType(String name) throws BadBytecode { - try { - return Type.get(classPool.get(name)); - } catch (NotFoundException e) { - throw new BadBytecode("Could not find class [pos = " + lastPos + "]: " + name); - } - } - - private Type[] paramTypesFromDesc(String desc) throws BadBytecode { - CtClass classes[] = null; - try { - classes = Descriptor.getParameterTypes(desc, classPool); - } catch (NotFoundException e) { - throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); - } - - if (classes == null) - throw new BadBytecode("Could not obtain parameters for descriptor [pos = " + lastPos + "]: " + desc); - - Type[] types = new Type[classes.length]; - for (int i = 0; i < types.length; i++) - types[i] = Type.get(classes[i]); - - return types; - } - - private Type returnTypeFromDesc(String desc) throws BadBytecode { - CtClass clazz = null; - try { - clazz = Descriptor.getReturnType(desc, classPool); - } catch (NotFoundException e) { - throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); - } - - if (clazz == null) - throw new BadBytecode("Could not obtain return type for descriptor [pos = " + lastPos + "]: " + desc); - - return Type.get(clazz); - } - - private Type simplePeek(Frame frame) { - Type type = frame.peek(); - return (type == Type.TOP) ? frame.getStack(frame.getTopIndex() - 1) : type; - } - - private Type simplePop(Frame frame) { - Type type = frame.pop(); - return (type == Type.TOP) ? frame.pop() : type; - } - - private void simplePush(Type type, Frame frame) { - frame.push(type); - if (type.getSize() == 2) - frame.push(Type.TOP); - } - - private void access(int index, Type type, Subroutine subroutine) { - if (subroutine == null) - return; - subroutine.access(index); - if (type.getSize() == 2) - subroutine.access(index + 1); - } - - private void simpleSetLocal(int index, Type type, Frame frame) { - frame.setLocal(index, type); - if (type.getSize() == 2) - frame.setLocal(index + 1, Type.TOP); - } - - private Type resolveClassInfo(String info) throws BadBytecode { - CtClass clazz = null; - try { - if (info.charAt(0) == '[') { - clazz = Descriptor.toCtClass(info, classPool); - } else { - clazz = classPool.get(info); - } - - } catch (NotFoundException e) { - throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); - } - - if (clazz == null) - throw new BadBytecode("Could not obtain type for descriptor [pos = " + lastPos + "]: " + info); - - return Type.get(clazz); - } - - private Type typeFromDesc(String desc) throws BadBytecode { - CtClass clazz = null; - try { - clazz = Descriptor.toCtClass(desc, classPool); - } catch (NotFoundException e) { - throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); - } - - if (clazz == null) - throw new BadBytecode("Could not obtain type for descriptor [pos = " + lastPos + "]: " + desc); - - return Type.get(clazz); - } - - private void verifyAssignable(Type expected, Type type) throws BadBytecode { - if (! expected.isAssignableFrom(type)) - throw new BadBytecode("Expected type: " + expected + " Got: " + type + " [pos = " + lastPos + "]"); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java deleted file mode 100644 index 10e87db..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - - -/** - * Represents the stack frame and local variable table at a particular point in time. - * - * @author Jason T. Greene - */ -public class Frame { - private Type[] locals; - private Type[] stack; - private int top; - private boolean jsrMerged; - private boolean retMerged; - - /** - * Create a new frame with the specified local variable table size, and max stack size - * - * @param locals the number of local variable table entries - * @param stack the maximum stack size - */ - public Frame(int locals, int stack) { - this.locals = new Type[locals]; - this.stack = new Type[stack]; - } - - /** - * Returns the local varaible table entry at index. - * - * @param index the position in the table - * @return the type if one exists, or null if the position is empty - */ - public Type getLocal(int index) { - return locals[index]; - } - - /** - * Sets the local variable table entry at index to a type. - * - * @param index the position in the table - * @param type the type to set at the position - */ - public void setLocal(int index, Type type) { - locals[index] = type; - } - - - /** - * Returns the type on the stack at the specified index. - * - * @param index the position on the stack - * @return the type of the stack position - */ - public Type getStack(int index) { - return stack[index]; - } - - /** - * Sets the type of the stack position - * - * @param index the position on the stack - * @param type the type to set - */ - public void setStack(int index, Type type) { - stack[index] = type; - } - - /** - * Empties the stack - */ - public void clearStack() { - top = 0; - } - - /** - * Gets the index of the type sitting at the top of the stack. - * This is not to be confused with a length operation which - * would return the number of elements, not the position of - * the last element. - * - * @return the position of the element at the top of the stack - */ - public int getTopIndex() { - return top - 1; - } - - /** - * Returns the number of local variable table entries, specified - * at construction. - * - * @return the number of local variable table entries - */ - public int localsLength() { - return locals.length; - } - - /** - * Gets the top of the stack without altering it - * - * @return the top of the stack - */ - public Type peek() { - if (top < 1) - throw new IndexOutOfBoundsException("Stack is empty"); - - return stack[top - 1]; - } - - /** - * Alters the stack to contain one less element and return it. - * - * @return the element popped from the stack - */ - public Type pop() { - if (top < 1) - throw new IndexOutOfBoundsException("Stack is empty"); - return stack[--top]; - } - - /** - * Alters the stack by placing the passed type on the top - * - * @param type the type to add to the top - */ - public void push(Type type) { - stack[top++] = type; - } - - - /** - * Makes a shallow copy of this frame, i.e. the type instances will - * remain the same. - * - * @return the shallow copy - */ - public Frame copy() { - Frame frame = new Frame(locals.length, stack.length); - System.arraycopy(locals, 0, frame.locals, 0, locals.length); - System.arraycopy(stack, 0, frame.stack, 0, stack.length); - frame.top = top; - return frame; - } - - /** - * Makes a shallow copy of the stack portion of this frame. The local - * variable table size will be copied, but its contents will be empty. - * - * @return the shallow copy of the stack - */ - public Frame copyStack() { - Frame frame = new Frame(locals.length, stack.length); - System.arraycopy(stack, 0, frame.stack, 0, stack.length); - frame.top = top; - return frame; - } - - /** - * Merges all types on the stack of this frame instance with that of the specified frame. - * The local variable table is left untouched. - * - * @param frame the frame to merge the stack from - * @return true if any changes where made - */ - public boolean mergeStack(Frame frame) { - boolean changed = false; - if (top != frame.top) - throw new RuntimeException("Operand stacks could not be merged, they are different sizes!"); - - for (int i = 0; i < top; i++) { - if (stack[i] != null) { - Type prev = stack[i]; - Type merged = prev.merge(frame.stack[i]); - if (merged == Type.BOGUS) - throw new RuntimeException("Operand stacks could not be merged due to differing primitive types: pos = " + i); - - stack[i] = merged; - // always replace the instance in case a multi-interface type changes to a normal Type - if ((! merged.equals(prev)) || merged.popChanged()) { - changed = true; - } - } - } - - return changed; - } - - /** - * Merges all types on the stack and local variable table of this frame with that of the specified - * type. - * - * @param frame the frame to merge with - * @return true if any changes to this frame where made by this merge - */ - public boolean merge(Frame frame) { - boolean changed = false; - - // Local variable table - for (int i = 0; i < locals.length; i++) { - if (locals[i] != null) { - Type prev = locals[i]; - Type merged = prev.merge(frame.locals[i]); - // always replace the instance in case a multi-interface type changes to a normal Type - locals[i] = merged; - if (! merged.equals(prev) || merged.popChanged()) { - changed = true; - } - } else if (frame.locals[i] != null) { - locals[i] = frame.locals[i]; - changed = true; - } - } - - changed |= mergeStack(frame); - return changed; - } - - public String toString() { - StringBuffer buffer = new StringBuffer(); - - buffer.append("locals = ["); - for (int i = 0; i < locals.length; i++) { - buffer.append(locals[i] == null ? "empty" : locals[i].toString()); - if (i < locals.length - 1) - buffer.append(", "); - } - buffer.append("] stack = ["); - for (int i = 0; i < top; i++) { - buffer.append(stack[i]); - if (i < top - 1) - buffer.append(", "); - } - buffer.append("]"); - - return buffer.toString(); - } - - /** - * Whether or not state from the source JSR instruction has been merged - * - * @return true if JSR state has been merged - */ - boolean isJsrMerged() { - return jsrMerged; - } - - /** - * Sets whether of not the state from the source JSR instruction has been merged - * - * @param jsrMerged true if merged, otherwise false - */ - void setJsrMerged(boolean jsrMerged) { - this.jsrMerged = jsrMerged; - } - - /** - * Whether or not state from the RET instruction, of the subroutine that was jumped - * to has been merged. - * - * @return true if RET state has been merged - */ - boolean isRetMerged() { - return retMerged; - } - - /** - * Sets whether or not state from the RET instruction, of the subroutine that was jumped - * to has been merged. - * - * @param retMerged true if RET state has been merged - */ - void setRetMerged(boolean retMerged) { - this.retMerged = retMerged; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java deleted file mode 100644 index 84bd21c..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import java.io.PrintStream; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.Modifier; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.CodeAttribute; -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.bytecode.InstructionPrinter; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; - -/** - * A utility class for printing a merged view of the frame state and the - * instructions of a method. - * - * @author Jason T. Greene - */ -public final class FramePrinter { - private final PrintStream stream; - - /** - * Constructs a bytecode printer. - */ - public FramePrinter(PrintStream stream) { - this.stream = stream; - } - - /** - * Prints all the methods declared in the given class. - */ - public static void print(CtClass clazz, PrintStream stream) { - (new FramePrinter(stream)).print(clazz); - } - - /** - * Prints all the methods declared in the given class. - */ - public void print(CtClass clazz) { - CtMethod[] methods = clazz.getDeclaredMethods(); - for (int i = 0; i < methods.length; i++) { - print(methods[i]); - } - } - - private String getMethodString(CtMethod method) { - try { - return Modifier.toString(method.getModifiers()) + " " - + method.getReturnType().getName() + " " + method.getName() - + Descriptor.toString(method.getSignature()) + ";"; - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - } - - /** - * Prints the instructions and the frame states of the given method. - */ - public void print(CtMethod method) { - stream.println("\n" + getMethodString(method)); - MethodInfo info = method.getMethodInfo2(); - ConstPool pool = info.getConstPool(); - CodeAttribute code = info.getCodeAttribute(); - if (code == null) - return; - - Frame[] frames; - try { - frames = (new Analyzer()).analyze(method.getDeclaringClass(), info); - } catch (BadBytecode e) { - throw new RuntimeException(e); - } - - int spacing = String.valueOf(code.getCodeLength()).length(); - - CodeIterator iterator = code.iterator(); - while (iterator.hasNext()) { - int pos; - try { - pos = iterator.next(); - } catch (BadBytecode e) { - throw new RuntimeException(e); - } - - stream.println(pos + ": " + InstructionPrinter.instructionString(iterator, pos, pool)); - - addSpacing(spacing + 3); - Frame frame = frames[pos]; - if (frame == null) { - stream.println("--DEAD CODE--"); - continue; - } - printStack(frame); - - addSpacing(spacing + 3); - printLocals(frame); - } - - } - - private void printStack(Frame frame) { - stream.print("stack ["); - int top = frame.getTopIndex(); - for (int i = 0; i <= top; i++) { - if (i > 0) - stream.print(", "); - Type type = frame.getStack(i); - stream.print(type); - } - stream.println("]"); - } - - private void printLocals(Frame frame) { - stream.print("locals ["); - int length = frame.localsLength(); - for (int i = 0; i < length; i++) { - if (i > 0) - stream.print(", "); - Type type = frame.getLocal(i); - stream.print(type == null ? "empty" : type.toString()); - } - stream.println("]"); - } - - private void addSpacing(int count) { - while (count-- > 0) - stream.print(' '); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java deleted file mode 100644 index 52e3b53..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import java.util.NoSuchElementException; - -class IntQueue { - private static class Entry { - private IntQueue.Entry next; - private int value; - private Entry(int value) { - this.value = value; - } - } - private IntQueue.Entry head; - - private IntQueue.Entry tail; - - void add(int value) { - IntQueue.Entry entry = new Entry(value); - if (tail != null) - tail.next = entry; - tail = entry; - - if (head == null) - head = entry; - } - - boolean isEmpty() { - return head == null; - } - - int take() { - if (head == null) - throw new NoSuchElementException(); - - int value = head.value; - head = head.next; - if (head == null) - tail = null; - - return value; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java deleted file mode 100644 index 8a9eefa..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.NotFoundException; - -/** - * Represents an array of {@link MultiType} instances. - * - * @author Jason T. Greene - */ -public class MultiArrayType extends Type { - private MultiType component; - private int dims; - - public MultiArrayType(MultiType component, int dims) { - super(null); - this.component = component; - this.dims = dims; - } - - public CtClass getCtClass() { - CtClass clazz = component.getCtClass(); - if (clazz == null) - return null; - - ClassPool pool = clazz.getClassPool(); - if (pool == null) - pool = ClassPool.getDefault(); - - String name = arrayName(clazz.getName(), dims); - - try { - return pool.get(name); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - } - - boolean popChanged() { - return component.popChanged(); - } - - public int getDimensions() { - return dims; - } - - public Type getComponent() { - return dims == 1 ? (Type)component : new MultiArrayType(component, dims - 1); - } - - public int getSize() { - return 1; - } - - public boolean isArray() { - return true; - } - - public boolean isAssignableFrom(Type type) { - throw new UnsupportedOperationException("Not implemented"); - } - - public boolean isReference() { - return true; - } - - public boolean isAssignableTo(Type type) { - if (eq(type.getCtClass(), Type.OBJECT.getCtClass())) - return true; - - if (eq(type.getCtClass(), Type.CLONEABLE.getCtClass())) - return true; - - if (eq(type.getCtClass(), Type.SERIALIZABLE.getCtClass())) - return true; - - if (! type.isArray()) - return false; - - Type typeRoot = getRootComponent(type); - int typeDims = type.getDimensions(); - - if (typeDims > dims) - return false; - - if (typeDims < dims) { - if (eq(typeRoot.getCtClass(), Type.OBJECT.getCtClass())) - return true; - - if (eq(typeRoot.getCtClass(), Type.CLONEABLE.getCtClass())) - return true; - - if (eq(typeRoot.getCtClass(), Type.SERIALIZABLE.getCtClass())) - return true; - - return false; - } - - return component.isAssignableTo(typeRoot); - } - - public boolean equals(Object o) { - if (! (o instanceof MultiArrayType)) - return false; - MultiArrayType multi = (MultiArrayType)o; - - return component.equals(multi.component) && dims == multi.dims; - } - - public String toString() { - // follows the same detailed formating scheme as component - return arrayName(component.toString(), dims); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java deleted file mode 100644 index 6158535..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import com.wenshuo.agent.javassist.CtClass; - -/** - * MultiType represents an unresolved type. Whenever two {@code Type} - * instances are merged, if they share more than one super type (either an - * interface or a superclass), then a {@code MultiType} is used to - * represent the possible super types. The goal of a {@code MultiType} - * is to reduce the set of possible types down to a single resolved type. This - * is done by eliminating non-assignable types from the typeset when the - * {@code MultiType} is passed as an argument to - * {@link Type#isAssignableFrom(Type)}, as well as removing non-intersecting - * types during a merge. - * - * Note: Currently the {@code MultiType} instance is reused as much - * as possible so that updates are visible from all frames. In addition, all - * {@code MultiType} merge paths are also updated. This is somewhat - * hackish, but it appears to handle most scenarios. - * - * @author Jason T. Greene - */ - -/* TODO - A better, but more involved, approach would be to track the instruction - * offset that resulted in the creation of this type, and - * whenever the typeset changes, to force a merge on that position. This - * would require creating a new MultiType instance every time the typeset - * changes, and somehow communicating assignment changes to the Analyzer - */ -public class MultiType extends Type { - private Map interfaces; - private Type resolved; - private Type potentialClass; - private MultiType mergeSource; - private boolean changed = false; - - public MultiType(Map interfaces) { - this(interfaces, null); - } - - public MultiType(Map interfaces, Type potentialClass) { - super(null); - this.interfaces = interfaces; - this.potentialClass = potentialClass; - } - - /** - * Gets the class that corresponds with this type. If this information - * is not yet known, java.lang.Object will be returned. - */ - public CtClass getCtClass() { - if (resolved != null) - return resolved.getCtClass(); - - return Type.OBJECT.getCtClass(); - } - - /** - * Always returns null since this type is never used for an array. - */ - public Type getComponent() { - return null; - } - - /** - * Always returns 1, since this type is a reference. - */ - public int getSize() { - return 1; - } - - /** - * Always reutnrs false since this type is never used for an array - */ - public boolean isArray() { - return false; - } - - /** - * Returns true if the internal state has changed. - */ - boolean popChanged() { - boolean changed = this.changed; - this.changed = false; - return changed; - } - - public boolean isAssignableFrom(Type type) { - throw new UnsupportedOperationException("Not implemented"); - } - - public boolean isAssignableTo(Type type) { - if (resolved != null) - return type.isAssignableFrom(resolved); - - if (Type.OBJECT.equals(type)) - return true; - - if (potentialClass != null && !type.isAssignableFrom(potentialClass)) - potentialClass = null; - - Map map = mergeMultiAndSingle(this, type); - - if (map.size() == 1 && potentialClass == null) { - // Update previous merge paths to the same resolved type - resolved = Type.get((CtClass)map.values().iterator().next()); - propogateResolved(); - - return true; - } - - // Keep all previous merge paths up to date - if (map.size() >= 1) { - interfaces = map; - propogateState(); - - return true; - } - - if (potentialClass != null) { - resolved = potentialClass; - propogateResolved(); - - return true; - } - - return false; - } - - private void propogateState() { - MultiType source = mergeSource; - while (source != null) { - source.interfaces = interfaces; - source.potentialClass = potentialClass; - source = source.mergeSource; - } - } - - private void propogateResolved() { - MultiType source = mergeSource; - while (source != null) { - source.resolved = resolved; - source = source.mergeSource; - } - } - - /** - * Always returns true, since this type is always a reference. - * - * @return true - */ - public boolean isReference() { - return true; - } - - private Map getAllMultiInterfaces(MultiType type) { - Map map = new HashMap(); - - Iterator iter = type.interfaces.values().iterator(); - while (iter.hasNext()) { - CtClass intf = (CtClass)iter.next(); - map.put(intf.getName(), intf); - getAllInterfaces(intf, map); - } - - return map; - } - - - private Map mergeMultiInterfaces(MultiType type1, MultiType type2) { - Map map1 = getAllMultiInterfaces(type1); - Map map2 = getAllMultiInterfaces(type2); - - return findCommonInterfaces(map1, map2); - } - - private Map mergeMultiAndSingle(MultiType multi, Type single) { - Map map1 = getAllMultiInterfaces(multi); - Map map2 = getAllInterfaces(single.getCtClass(), null); - - return findCommonInterfaces(map1, map2); - } - - private boolean inMergeSource(MultiType source) { - while (source != null) { - if (source == this) - return true; - - source = source.mergeSource; - } - - return false; - } - - public Type merge(Type type) { - if (this == type) - return this; - - if (type == UNINIT) - return this; - - if (type == BOGUS) - return BOGUS; - - if (type == null) - return this; - - if (resolved != null) - return resolved.merge(type); - - if (potentialClass != null) { - Type mergePotential = potentialClass.merge(type); - if (! mergePotential.equals(potentialClass) || mergePotential.popChanged()) { - potentialClass = Type.OBJECT.equals(mergePotential) ? null : mergePotential; - changed = true; - } - } - - Map merged; - - if (type instanceof MultiType) { - MultiType multi = (MultiType)type; - - if (multi.resolved != null) { - merged = mergeMultiAndSingle(this, multi.resolved); - } else { - merged = mergeMultiInterfaces(multi, this); - if (! inMergeSource(multi)) - mergeSource = multi; - } - } else { - merged = mergeMultiAndSingle(this, type); - } - - // Keep all previous merge paths up to date - if (merged.size() > 1 || (merged.size() == 1 && potentialClass != null)) { - // Check for changes - if (merged.size() != interfaces.size()) { - changed = true; - } else if (changed == false){ - Iterator iter = merged.keySet().iterator(); - while (iter.hasNext()) - if (! interfaces.containsKey(iter.next())) - changed = true; - } - - interfaces = merged; - propogateState(); - - return this; - } - - if (merged.size() == 1) { - resolved = Type.get((CtClass) merged.values().iterator().next()); - } else if (potentialClass != null){ - resolved = potentialClass; - } else { - resolved = OBJECT; - } - - propogateResolved(); - - return resolved; - } - - public boolean equals(Object o) { - if (! (o instanceof MultiType)) - return false; - - MultiType multi = (MultiType) o; - if (resolved != null) - return resolved.equals(multi.resolved); - else if (multi.resolved != null) - return false; - - return interfaces.keySet().equals(multi.interfaces.keySet()); - } - - public String toString() { - if (resolved != null) - return resolved.toString(); - - StringBuffer buffer = new StringBuffer("{"); - Iterator iter = interfaces.keySet().iterator(); - while (iter.hasNext()) { - buffer.append(iter.next()); - buffer.append(", "); - } - buffer.setLength(buffer.length() - 2); - if (potentialClass != null) - buffer.append(", *").append(potentialClass.toString()); - buffer.append("}"); - return buffer.toString(); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java deleted file mode 100644 index 2f6f254..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Represents a nested method subroutine (marked by JSR and RET). - * - * @author Jason T. Greene - */ -public class Subroutine { - //private Set callers = new HashSet(); - private List callers = new ArrayList(); - private Set access = new HashSet(); - private int start; - - public Subroutine(int start, int caller) { - this.start = start; - callers.add(new Integer(caller)); - } - - public void addCaller(int caller) { - callers.add(new Integer(caller)); - } - - public int start() { - return start; - } - - public void access(int index) { - access.add(new Integer(index)); - } - - public boolean isAccessed(int index) { - return access.contains(new Integer(index)); - } - - public Collection accessed() { - return access; - } - - public Collection callers() { - return callers; - } - - public String toString() { - return "start = " + start + " callers = " + callers.toString(); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java deleted file mode 100644 index 2b43955..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.CodeAttribute; -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.ExceptionTable; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; -import com.wenshuo.agent.javassist.bytecode.Opcode; - -/** - * Discovers the subroutines in a method, and tracks all callers. - * - * @author Jason T. Greene - */ -public class SubroutineScanner implements Opcode { - - private Subroutine[] subroutines; - Map subTable = new HashMap(); - Set done = new HashSet(); - - - public Subroutine[] scan(MethodInfo method) throws BadBytecode { - CodeAttribute code = method.getCodeAttribute(); - CodeIterator iter = code.iterator(); - - subroutines = new Subroutine[code.getCodeLength()]; - subTable.clear(); - done.clear(); - - scan(0, iter, null); - - ExceptionTable exceptions = code.getExceptionTable(); - for (int i = 0; i < exceptions.size(); i++) { - int handler = exceptions.handlerPc(i); - // If an exception is thrown in subroutine, the handler - // is part of the same subroutine. - scan(handler, iter, subroutines[exceptions.startPc(i)]); - } - - return subroutines; - } - - private void scan(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { - // Skip already processed blocks - if (done.contains(new Integer(pos))) - return; - - done.add(new Integer(pos)); - - int old = iter.lookAhead(); - iter.move(pos); - - boolean next; - do { - pos = iter.next(); - next = scanOp(pos, iter, sub) && iter.hasNext(); - } while (next); - - iter.move(old); - } - - private boolean scanOp(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { - subroutines[pos] = sub; - - int opcode = iter.byteAt(pos); - - if (opcode == TABLESWITCH) { - scanTableSwitch(pos, iter, sub); - - return false; - } - - if (opcode == LOOKUPSWITCH) { - scanLookupSwitch(pos, iter, sub); - - return false; - } - - // All forms of return and throw end current code flow - if (Util.isReturn(opcode) || opcode == RET || opcode == ATHROW) - return false; - - if (Util.isJumpInstruction(opcode)) { - int target = Util.getJumpTarget(pos, iter); - if (opcode == JSR || opcode == JSR_W) { - Subroutine s = (Subroutine) subTable.get(new Integer(target)); - if (s == null) { - s = new Subroutine(target, pos); - subTable.put(new Integer(target), s); - scan(target, iter, s); - } else { - s.addCaller(pos); - } - } else { - scan(target, iter, sub); - - // GOTO ends current code flow - if (Util.isGoto(opcode)) - return false; - } - } - - return true; - } - - private void scanLookupSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { - int index = (pos & ~3) + 4; - // default - scan(pos + iter.s32bitAt(index), iter, sub); - int npairs = iter.s32bitAt(index += 4); - int end = npairs * 8 + (index += 4); - - // skip "match" - for (index += 4; index < end; index += 8) { - int target = iter.s32bitAt(index) + pos; - scan(target, iter, sub); - } - } - - private void scanTableSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { - // Skip 4 byte alignment padding - int index = (pos & ~3) + 4; - // default - scan(pos + iter.s32bitAt(index), iter, sub); - int low = iter.s32bitAt(index += 4); - int high = iter.s32bitAt(index += 4); - int end = (high - low + 1) * 4 + (index += 4); - - // Offset table - for (; index < end; index += 4) { - int target = iter.s32bitAt(index) + pos; - scan(target, iter, sub); - } - } - - -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/Type.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/Type.java deleted file mode 100644 index 41b36b3..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/Type.java +++ /dev/null @@ -1,593 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.Map; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.NotFoundException; - -/** - * Represents a JVM type in data-flow analysis. This abstraction is necessary since - * a JVM type not only includes all normal Java types, but also a few special types - * that are used by the JVM internally. See the static field types on this class for - * more info on these special types. - * - * All primitive and special types reuse the same instance, so identity comparison can - * be used when examining them. Normal java types must use {@link #equals(Object)} to - * compare type instances. - * - * In most cases, applications which consume this API, only need to call {@link #getCtClass()} - * to obtain the needed type information. - * - * @author Jason T. Greene - */ -public class Type { - private final CtClass clazz; - private final boolean special; - - private static final Map prims = new IdentityHashMap(); - /** Represents the double primitive type */ - public static final Type DOUBLE = new Type(CtClass.doubleType); - /** Represents the boolean primitive type */ - public static final Type BOOLEAN = new Type(CtClass.booleanType); - /** Represents the long primitive type */ - public static final Type LONG = new Type(CtClass.longType); - /** Represents the char primitive type */ - public static final Type CHAR = new Type(CtClass.charType); - /** Represents the byte primitive type */ - public static final Type BYTE = new Type(CtClass.byteType); - /** Represents the short primitive type */ - public static final Type SHORT = new Type(CtClass.shortType); - /** Represents the integer primitive type */ - public static final Type INTEGER = new Type(CtClass.intType); - /** Represents the float primitive type */ - public static final Type FLOAT = new Type(CtClass.floatType); - /** Represents the void primitive type */ - public static final Type VOID = new Type(CtClass.voidType); - - /** - * Represents an unknown, or null type. This occurs when aconst_null is used. - * It is important not to treat this type as java.lang.Object, since a null can - * be assigned to any reference type. The analyzer will replace these with - * an actual known type if it can be determined by a merged path with known type - * information. If this type is encountered on a frame then it is guaranteed to - * be null, and the type information is simply not available. Any attempts to - * infer the type, without further information from the compiler would be a guess. - */ - public static final Type UNINIT = new Type(null); - - /** - * Represents an internal JVM return address, which is used by the RET - * instruction to return to a JSR that invoked the subroutine. - */ - public static final Type RETURN_ADDRESS = new Type(null, true); - - /** A placeholder used by the analyzer for the second word position of a double-word type */ - public static final Type TOP = new Type(null, true); - - /** - * Represents a non-accessible value. Code cannot access the value this type - * represents. It occurs when bytecode reuses a local variable table - * position with non-mergable types. An example would be compiled code which - * uses the same position for a primitive type in one branch, and a reference type - * in another branch. - */ - public static final Type BOGUS = new Type(null, true); - - /** Represents the java.lang.Object reference type */ - public static final Type OBJECT = lookupType("java.lang.Object"); - /** Represents the java.io.Serializable reference type */ - public static final Type SERIALIZABLE = lookupType("java.io.Serializable"); - /** Represents the java.lang.Coneable reference type */ - public static final Type CLONEABLE = lookupType("java.lang.Cloneable"); - /** Represents the java.lang.Throwable reference type */ - public static final Type THROWABLE = lookupType("java.lang.Throwable"); - - static { - prims.put(CtClass.doubleType, DOUBLE); - prims.put(CtClass.longType, LONG); - prims.put(CtClass.charType, CHAR); - prims.put(CtClass.shortType, SHORT); - prims.put(CtClass.intType, INTEGER); - prims.put(CtClass.floatType, FLOAT); - prims.put(CtClass.byteType, BYTE); - prims.put(CtClass.booleanType, BOOLEAN); - prims.put(CtClass.voidType, VOID); - - } - - /** - * Obtain the Type for a given class. If the class is a primitive, - * the the unique type instance for the primitive will be returned. - * Otherwise a new Type instance representing the class is returned. - * - * @param clazz The java class - * @return a type instance for this class - */ - public static Type get(CtClass clazz) { - Type type = (Type)prims.get(clazz); - return type != null ? type : new Type(clazz); - } - - private static Type lookupType(String name) { - try { - return new Type(ClassPool.getDefault().get(name)); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - } - - Type(CtClass clazz) { - this(clazz, false); - } - - private Type(CtClass clazz, boolean special) { - this.clazz = clazz; - this.special = special; - } - - // Used to indicate a merge internally triggered a change - boolean popChanged() { - return false; - } - - /** - * Gets the word size of this type. Double-word types, such as long and double - * will occupy two positions on the local variable table or stack. - * - * @return the number of words needed to hold this type - */ - public int getSize() { - return clazz == CtClass.doubleType || clazz == CtClass.longType || this == TOP ? 2 : 1; - } - - /** - * Returns the class this type represents. If the type is special, null will be returned. - * - * @return the class for this type, or null if special - */ - public CtClass getCtClass() { - return clazz; - } - - /** - * Returns whether or not this type is a normal java reference, i.e. it is or extends java.lang.Object. - * - * @return true if a java reference, false if a primitive or special - */ - public boolean isReference() { - return !special && (clazz == null || !clazz.isPrimitive()); - } - - /** - * Returns whether or not the type is special. A special type is one that is either used - * for internal tracking, or is only used internally by the JVM. - * - * @return true if special, false if not - */ - public boolean isSpecial() { - return special; - } - - /** - * Returns whether or not this type is an array. - * - * @return true if an array, false if not - */ - public boolean isArray() { - return clazz != null && clazz.isArray(); - } - - /** - * Returns the number of dimensions of this array. If the type is not an - * array zero is returned. - * - * @return zero if not an array, otherwise the number of array dimensions. - */ - public int getDimensions() { - if (!isArray()) return 0; - - String name = clazz.getName(); - int pos = name.length() - 1; - int count = 0; - while (name.charAt(pos) == ']' ) { - pos -= 2; - count++; - } - - return count; - } - - /** - * Returns the array component if this type is an array. If the type - * is not an array null is returned. - * - * @return the array component if an array, otherwise null - */ - public Type getComponent() { - if (this.clazz == null || !this.clazz.isArray()) - return null; - - CtClass component; - try { - component = this.clazz.getComponentType(); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - - Type type = (Type)prims.get(component); - return (type != null) ? type : new Type(component); - } - - /** - * Determines whether this type is assignable, to the passed type. - * A type is assignable to another if it is either the same type, or - * a sub-type. - * - * @param type the type to test assignability to - * @return true if this is assignable to type, otherwise false - */ - public boolean isAssignableFrom(Type type) { - if (this == type) - return true; - - if ((type == UNINIT && isReference()) || this == UNINIT && type.isReference()) - return true; - - if (type instanceof MultiType) - return ((MultiType)type).isAssignableTo(this); - - if (type instanceof MultiArrayType) - return ((MultiArrayType)type).isAssignableTo(this); - - - // Primitives and Special types must be identical - if (clazz == null || clazz.isPrimitive()) - return false; - - try { - return type.clazz.subtypeOf(clazz); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Finds the common base type, or interface which both this and the specified - * type can be assigned. If there is more than one possible answer, then a {@link MultiType}, - * or a {@link MultiArrayType} is returned. Multi-types have special rules, - * and successive merges and assignment tests on them will alter their internal state, - * as well as other multi-types they have been merged with. This method is used by - * the data-flow analyzer to merge the type state from multiple branches. - * - * @param type the type to merge with - * @return the merged type - */ - public Type merge(Type type) { - if (type == this) - return this; - if (type == null) - return this; - if (type == Type.UNINIT) - return this; - if (this == Type.UNINIT) - return type; - - // Unequal primitives and special types can not be merged - if (! type.isReference() || ! this.isReference()) - return BOGUS; - - // Centralize merging of multi-interface types - if (type instanceof MultiType) - return type.merge(this); - - if (type.isArray() && this.isArray()) - return mergeArray(type); - - try { - return mergeClasses(type); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - } - - Type getRootComponent(Type type) { - while (type.isArray()) - type = type.getComponent(); - - return type; - } - - private Type createArray(Type rootComponent, int dims) { - if (rootComponent instanceof MultiType) - return new MultiArrayType((MultiType) rootComponent, dims); - - String name = arrayName(rootComponent.clazz.getName(), dims); - - Type type; - try { - type = Type.get(getClassPool(rootComponent).get(name)); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - - return type; - } - - String arrayName(String component, int dims) { - // Using char[] since we have no StringBuilder in JDK4, and StringBuffer is slow. - // Although, this is more efficient even if we did have one. - int i = component.length(); - int size = i + dims * 2; - char[] string = new char[size]; - component.getChars(0, i, string, 0); - while (i < size) { - string[i++] = '['; - string[i++] = ']'; - } - component = new String(string); - return component; - } - - private ClassPool getClassPool(Type rootComponent) { - ClassPool pool = rootComponent.clazz.getClassPool(); - return pool != null ? pool : ClassPool.getDefault(); - } - - private Type mergeArray(Type type) { - Type typeRoot = getRootComponent(type); - Type thisRoot = getRootComponent(this); - int typeDims = type.getDimensions(); - int thisDims = this.getDimensions(); - - // Array commponents can be merged when the dimensions are equal - if (typeDims == thisDims) { - Type mergedComponent = thisRoot.merge(typeRoot); - - // If the components can not be merged (a primitive component mixed with a different type) - // then Object is the common type. - if (mergedComponent == Type.BOGUS) - return Type.OBJECT; - - return createArray(mergedComponent, thisDims); - } - - Type targetRoot; - int targetDims; - - if (typeDims < thisDims) { - targetRoot = typeRoot; - targetDims = typeDims; - } else { - targetRoot = thisRoot; - targetDims = thisDims; - } - - // Special case, arrays are cloneable and serializable, so prefer them when dimensions differ - if (eq(CLONEABLE.clazz, targetRoot.clazz) || eq(SERIALIZABLE.clazz, targetRoot.clazz)) - return createArray(targetRoot, targetDims); - - return createArray(OBJECT, targetDims); - } - - private static CtClass findCommonSuperClass(CtClass one, CtClass two) throws NotFoundException { - CtClass deep = one; - CtClass shallow = two; - CtClass backupShallow = shallow; - CtClass backupDeep = deep; - - // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly - for (;;) { - // In case we get lucky, and find a match early - if (eq(deep, shallow) && deep.getSuperclass() != null) - return deep; - - CtClass deepSuper = deep.getSuperclass(); - CtClass shallowSuper = shallow.getSuperclass(); - - if (shallowSuper == null) { - // right, now reset shallow - shallow = backupShallow; - break; - } - - if (deepSuper == null) { - // wrong, swap them, since deep is now useless, its our tmp before we swap it - deep = backupDeep; - backupDeep = backupShallow; - backupShallow = deep; - - deep = shallow; - shallow = backupShallow; - break; - } - - deep = deepSuper; - shallow = shallowSuper; - } - - // Phase 2 - Move deepBackup up by (deep end - deep) - for (;;) { - deep = deep.getSuperclass(); - if (deep == null) - break; - - backupDeep = backupDeep.getSuperclass(); - } - - deep = backupDeep; - - // Phase 3 - The hierarchy positions are now aligned - // The common super class is easy to find now - while (!eq(deep, shallow)) { - deep = deep.getSuperclass(); - shallow = shallow.getSuperclass(); - } - - return deep; - } - - private Type mergeClasses(Type type) throws NotFoundException { - CtClass superClass = findCommonSuperClass(this.clazz, type.clazz); - - // If its Object, then try and find a common interface(s) - if (superClass.getSuperclass() == null) { - Map interfaces = findCommonInterfaces(type); - if (interfaces.size() == 1) - return new Type((CtClass) interfaces.values().iterator().next()); - if (interfaces.size() > 1) - return new MultiType(interfaces); - - // Only Object is in common - return new Type(superClass); - } - - // Check for a common interface that is not on the found supertype - Map commonDeclared = findExclusiveDeclaredInterfaces(type, superClass); - if (commonDeclared.size() > 0) { - return new MultiType(commonDeclared, new Type(superClass)); - } - - return new Type(superClass); - } - - private Map findCommonInterfaces(Type type) { - Map typeMap = getAllInterfaces(type.clazz, null); - Map thisMap = getAllInterfaces(this.clazz, null); - - return findCommonInterfaces(typeMap, thisMap); - } - - private Map findExclusiveDeclaredInterfaces(Type type, CtClass exclude) { - Map typeMap = getDeclaredInterfaces(type.clazz, null); - Map thisMap = getDeclaredInterfaces(this.clazz, null); - Map excludeMap = getAllInterfaces(exclude, null); - - Iterator i = excludeMap.keySet().iterator(); - while (i.hasNext()) { - Object intf = i.next(); - typeMap.remove(intf); - thisMap.remove(intf); - } - - return findCommonInterfaces(typeMap, thisMap); - } - - - Map findCommonInterfaces(Map typeMap, Map alterMap) { - Iterator i = alterMap.keySet().iterator(); - while (i.hasNext()) { - if (! typeMap.containsKey(i.next())) - i.remove(); - } - - // Reduce to subinterfaces - // This does not need to be recursive since we make a copy, - // and that copy contains all super types for the whole hierarchy - i = new ArrayList(alterMap.values()).iterator(); - while (i.hasNext()) { - CtClass intf = (CtClass) i.next(); - CtClass[] interfaces; - try { - interfaces = intf.getInterfaces(); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - - for (int c = 0; c < interfaces.length; c++) - alterMap.remove(interfaces[c].getName()); - } - - return alterMap; - } - - Map getAllInterfaces(CtClass clazz, Map map) { - if (map == null) - map = new HashMap(); - - if (clazz.isInterface()) - map.put(clazz.getName(), clazz); - do { - try { - CtClass[] interfaces = clazz.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - CtClass intf = interfaces[i]; - map.put(intf.getName(), intf); - getAllInterfaces(intf, map); - } - - clazz = clazz.getSuperclass(); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - } while (clazz != null); - - return map; - } - - Map getDeclaredInterfaces(CtClass clazz, Map map) { - if (map == null) - map = new HashMap(); - - if (clazz.isInterface()) - map.put(clazz.getName(), clazz); - - CtClass[] interfaces; - try { - interfaces = clazz.getInterfaces(); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } - - for (int i = 0; i < interfaces.length; i++) { - CtClass intf = interfaces[i]; - map.put(intf.getName(), intf); - getDeclaredInterfaces(intf, map); - } - - return map; - } - - public boolean equals(Object o) { - if (! (o instanceof Type)) - return false; - - return o.getClass() == getClass() && eq(clazz, ((Type)o).clazz); - } - - static boolean eq(CtClass one, CtClass two) { - return one == two || (one != null && two != null && one.getName().equals(two.getName())); - } - - public String toString() { - if (this == BOGUS) - return "BOGUS"; - if (this == UNINIT) - return "UNINIT"; - if (this == RETURN_ADDRESS) - return "RETURN ADDRESS"; - if (this == TOP) - return "TOP"; - - return clazz == null ? "null" : clazz.getName(); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/Util.java b/src/com/wenshuo/agent/javassist/bytecode/analysis/Util.java deleted file mode 100644 index e935310..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/Util.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.analysis; - -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.Opcode; - -/** - * A set of common utility methods. - * - * @author Jason T. Greene - */ -public class Util implements Opcode { - public static int getJumpTarget(int pos, CodeIterator iter) { - int opcode = iter.byteAt(pos); - pos += (opcode == JSR_W || opcode == GOTO_W) ? iter.s32bitAt(pos + 1) : iter.s16bitAt(pos + 1); - return pos; - } - - public static boolean isJumpInstruction(int opcode) { - return (opcode >= IFEQ && opcode <= JSR) || opcode == IFNULL || opcode == IFNONNULL || opcode == JSR_W || opcode == GOTO_W; - } - - public static boolean isGoto(int opcode) { - return opcode == GOTO || opcode == GOTO_W; - } - - public static boolean isJsr(int opcode) { - return opcode == JSR || opcode == JSR_W; - } - - public static boolean isReturn(int opcode) { - return (opcode >= IRETURN && opcode <= RETURN); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/analysis/package.html b/src/com/wenshuo/agent/javassist/bytecode/analysis/package.html deleted file mode 100644 index ec39a47..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/analysis/package.html +++ /dev/null @@ -1,20 +0,0 @@ - - -Bytecode Analysis API. - -

This package provides an API for performing data-flow analysis on a method's bytecode. -This allows the user to determine the type state of the stack and local variable table -at the start of every instruction. In addition this API can be used to validate -bytecode, find dead bytecode, and identify unnecessary checkcasts. -Look at ControlFlow class first for details. - -

The users of this package must know the specifications of -class file and Java bytecode. For more details, read this book: - -

    Tim Lindholm and Frank Yellin, -"The Java Virtual Machine Specification 2nd Ed.", -Addison-Wesley, 1999. -
- - - diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java deleted file mode 100644 index fa93f9a..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.NotFoundException; - -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Set; -import java.util.Iterator; - -/** - * The annotation structure. - * - *

An instance of this class is returned by - * getAnnotations() in AnnotationsAttribute - * or in ParameterAnnotationsAttribute. - * - * @see javassist.bytecode.AnnotationsAttribute#getAnnotations() - * @see javassist.bytecode.ParameterAnnotationsAttribute#getAnnotations() - * @see MemberValue - * @see MemberValueVisitor - * @see AnnotationsWriter - * - * @author Bill Burke - * @author Shigeru Chiba - * @author Adrian Brock - */ -public class Annotation { - static class Pair { - int name; - MemberValue value; - } - - ConstPool pool; - int typeIndex; - LinkedHashMap members; // this sould be LinkedHashMap - // but it is not supported by JDK 1.3. - - /** - * Constructs an annotation including no members. A member can be - * later added to the created annotation by addMemberValue(). - * - * @param type the index into the constant pool table. - * the entry at that index must be the - * CONSTANT_Utf8_Info structure - * repreenting the name of the annotation interface type. - * @param cp the constant pool table. - * - * @see #addMemberValue(String, MemberValue) - */ - public Annotation(int type, ConstPool cp) { - pool = cp; - typeIndex = type; - members = null; - } - - /** - * Constructs an annotation including no members. A member can be - * later added to the created annotation by addMemberValue(). - * - * @param typeName the name of the annotation interface type. - * @param cp the constant pool table. - * - * @see #addMemberValue(String, MemberValue) - */ - public Annotation(String typeName, ConstPool cp) { - this(cp.addUtf8Info(Descriptor.of(typeName)), cp); - } - - /** - * Constructs an annotation that can be accessed through the interface - * represented by clazz. The values of the members are - * not specified. - * - * @param cp the constant pool table. - * @param clazz the interface. - * @throws NotFoundException when the clazz is not found - */ - public Annotation(ConstPool cp, CtClass clazz) - throws NotFoundException - { - // todo Enums are not supported right now. - this(cp.addUtf8Info(Descriptor.of(clazz.getName())), cp); - - if (!clazz.isInterface()) - throw new RuntimeException( - "Only interfaces are allowed for Annotation creation."); - - CtMethod methods[] = clazz.getDeclaredMethods(); - if (methods.length > 0) { - members = new LinkedHashMap(); - } - - for (int i = 0; i < methods.length; i++) { - CtClass returnType = methods[i].getReturnType(); - addMemberValue(methods[i].getName(), - createMemberValue(cp, returnType)); - - } - } - - /** - * Makes an instance of MemberValue. - * - * @param cp the constant pool table. - * @param type the type of the member. - * @return the member value - * @throws NotFoundException when the type is not found - */ - public static MemberValue createMemberValue(ConstPool cp, CtClass type) - throws NotFoundException - { - if (type == CtClass.booleanType) - return new BooleanMemberValue(cp); - else if (type == CtClass.byteType) - return new ByteMemberValue(cp); - else if (type == CtClass.charType) - return new CharMemberValue(cp); - else if (type == CtClass.shortType) - return new ShortMemberValue(cp); - else if (type == CtClass.intType) - return new IntegerMemberValue(cp); - else if (type == CtClass.longType) - return new LongMemberValue(cp); - else if (type == CtClass.floatType) - return new FloatMemberValue(cp); - else if (type == CtClass.doubleType) - return new DoubleMemberValue(cp); - else if (type.getName().equals("java.lang.Class")) - return new ClassMemberValue(cp); - else if (type.getName().equals("java.lang.String")) - return new StringMemberValue(cp); - else if (type.isArray()) { - CtClass arrayType = type.getComponentType(); - MemberValue member = createMemberValue(cp, arrayType); - return new ArrayMemberValue(member, cp); - } - else if (type.isInterface()) { - Annotation info = new Annotation(cp, type); - return new AnnotationMemberValue(info, cp); - } - else { - // treat as enum. I know this is not typed, - // but JBoss has an Annotation Compiler for JDK 1.4 - // and I want it to work with that. - Bill Burke - EnumMemberValue emv = new EnumMemberValue(cp); - emv.setType(type.getName()); - return emv; - } - } - - /** - * Adds a new member. - * - * @param nameIndex the index into the constant pool table. - * The entry at that index must be - * a CONSTANT_Utf8_info structure. - * structure representing the member name. - * @param value the member value. - */ - public void addMemberValue(int nameIndex, MemberValue value) { - Pair p = new Pair(); - p.name = nameIndex; - p.value = value; - addMemberValue(p); - } - - /** - * Adds a new member. - * - * @param name the member name. - * @param value the member value. - */ - public void addMemberValue(String name, MemberValue value) { - Pair p = new Pair(); - p.name = pool.addUtf8Info(name); - p.value = value; - if (members == null) - members = new LinkedHashMap(); - - members.put(name, p); - } - - private void addMemberValue(Pair pair) { - String name = pool.getUtf8Info(pair.name); - if (members == null) - members = new LinkedHashMap(); - - members.put(name, pair); - } - - /** - * Returns a string representation of the annotation. - */ - public String toString() { - StringBuffer buf = new StringBuffer("@"); - buf.append(getTypeName()); - if (members != null) { - buf.append("("); - Iterator mit = members.keySet().iterator(); - while (mit.hasNext()) { - String name = (String)mit.next(); - buf.append(name).append("=").append(getMemberValue(name)); - if (mit.hasNext()) - buf.append(", "); - } - buf.append(")"); - } - - return buf.toString(); - } - - /** - * Obtains the name of the annotation type. - * - * @return the type name - */ - public String getTypeName() { - return Descriptor.toClassName(pool.getUtf8Info(typeIndex)); - } - - /** - * Obtains all the member names. - * - * @return null if no members are defined. - */ - public Set getMemberNames() { - if (members == null) - return null; - else - return members.keySet(); - } - - /** - * Obtains the member value with the given name. - * - *

If this annotation does not have a value for the - * specified member, - * this method returns null. It does not return a - * MemberValue with the default value. - * The default value can be obtained from the annotation type. - * - * @param name the member name - * @return null if the member cannot be found or if the value is - * the default value. - * - * @see javassist.bytecode.AnnotationDefaultAttribute - */ - public MemberValue getMemberValue(String name) { - if (members == null) - return null; - else { - Pair p = (Pair)members.get(name); - if (p == null) - return null; - else - return p.value; - } - } - - /** - * Constructs an annotation-type object representing this annotation. - * For example, if this annotation represents @Author, - * this method returns an Author object. - * - * @param cl class loader for loading an annotation type. - * @param cp class pool for obtaining class files. - * @return the annotation - * @throws ClassNotFoundException if the class cannot found. - * @throws NoSuchClassError if the class linkage fails. - */ - public Object toAnnotationType(ClassLoader cl, ClassPool cp) - throws ClassNotFoundException, NoSuchClassError - { - return AnnotationImpl.make(cl, - MemberValue.loadClass(cl, getTypeName()), - cp, this); - } - - /** - * Writes this annotation. - * - * @param writer the output. - * @throws IOException for an error during the write - */ - public void write(AnnotationsWriter writer) throws IOException { - String typeName = pool.getUtf8Info(typeIndex); - if (members == null) { - writer.annotation(typeName, 0); - return; - } - - writer.annotation(typeName, members.size()); - Iterator it = members.values().iterator(); - while (it.hasNext()) { - Pair pair = (Pair)it.next(); - writer.memberValuePair(pair.name); - pair.value.write(writer); - } - } - - /** - * Returns true if the given object represents the same annotation - * as this object. The equality test checks the member values. - */ - public boolean equals(Object obj) { - if (obj == this) - return true; - if (obj == null || obj instanceof Annotation == false) - return false; - - Annotation other = (Annotation) obj; - - if (getTypeName().equals(other.getTypeName()) == false) - return false; - - LinkedHashMap otherMembers = other.members; - if (members == otherMembers) - return true; - else if (members == null) - return otherMembers == null; - else - if (otherMembers == null) - return false; - else - return members.equals(otherMembers); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java deleted file mode 100644 index faf72ad..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.AnnotationDefaultAttribute; -import com.wenshuo.agent.javassist.bytecode.ClassFile; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; - -/** - * Internal-use only. This is a helper class internally used for implementing - * toAnnotationType() in Annotation. - * - * @author Shigeru Chiba - * @author Bill Burke - * @author Adrian Brock - */ -public class AnnotationImpl implements InvocationHandler { - private static final String JDK_ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation"; - private static Method JDK_ANNOTATION_TYPE_METHOD = null; - - private Annotation annotation; - private ClassPool pool; - private ClassLoader classLoader; - private transient Class annotationType; - private transient int cachedHashCode = Integer.MIN_VALUE; - - static { - // Try to resolve the JDK annotation type method - try { - Class clazz = Class.forName(JDK_ANNOTATION_CLASS_NAME); - JDK_ANNOTATION_TYPE_METHOD = clazz.getMethod("annotationType", (Class[])null); - } - catch (Exception ignored) { - // Probably not JDK5+ - } - } - - /** - * Constructs an annotation object. - * - * @param cl class loader for obtaining annotation types. - * @param clazz the annotation type. - * @param cp class pool for containing an annotation - * type (or null). - * @param anon the annotation. - * @return the annotation - */ - public static Object make(ClassLoader cl, Class clazz, ClassPool cp, - Annotation anon) { - AnnotationImpl handler = new AnnotationImpl(anon, cp, cl); - return Proxy.newProxyInstance(cl, new Class[] { clazz }, handler); - } - - private AnnotationImpl(Annotation a, ClassPool cp, ClassLoader loader) { - annotation = a; - pool = cp; - classLoader = loader; - } - - /** - * Obtains the name of the annotation type. - * - * @return the type name - */ - public String getTypeName() { - return annotation.getTypeName(); - } - - /** - * Get the annotation type - * - * @return the annotation class - * @throws NoClassDefFoundError when the class could not loaded - */ - private Class getAnnotationType() { - if (annotationType == null) { - String typeName = annotation.getTypeName(); - try { - annotationType = classLoader.loadClass(typeName); - } - catch (ClassNotFoundException e) { - NoClassDefFoundError error = new NoClassDefFoundError("Error loading annotation class: " + typeName); - error.setStackTrace(e.getStackTrace()); - throw error; - } - } - return annotationType; - } - - /** - * Obtains the internal data structure representing the annotation. - * - * @return the annotation - */ - public Annotation getAnnotation() { - return annotation; - } - - /** - * Executes a method invocation on a proxy instance. - * The implementations of toString(), equals(), - * and hashCode() are directly supplied by the - * AnnotationImpl. The annotationType() method - * is also available on the proxy instance. - */ - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable - { - String name = method.getName(); - if (Object.class == method.getDeclaringClass()) { - if ("equals".equals(name)) { - Object obj = args[0]; - return new Boolean(checkEquals(obj)); - } - else if ("toString".equals(name)) - return annotation.toString(); - else if ("hashCode".equals(name)) - return new Integer(hashCode()); - } - else if ("annotationType".equals(name) - && method.getParameterTypes().length == 0) - return getAnnotationType(); - - MemberValue mv = annotation.getMemberValue(name); - if (mv == null) - return getDefault(name, method); - else - return mv.getValue(classLoader, pool, method); - } - - private Object getDefault(String name, Method method) - throws ClassNotFoundException, RuntimeException - { - String classname = annotation.getTypeName(); - if (pool != null) { - try { - CtClass cc = pool.get(classname); - ClassFile cf = cc.getClassFile2(); - MethodInfo minfo = cf.getMethod(name); - if (minfo != null) { - AnnotationDefaultAttribute ainfo - = (AnnotationDefaultAttribute) - minfo.getAttribute(AnnotationDefaultAttribute.tag); - if (ainfo != null) { - MemberValue mv = ainfo.getDefaultValue(); - return mv.getValue(classLoader, pool, method); - } - } - } - catch (NotFoundException e) { - throw new RuntimeException("cannot find a class file: " - + classname); - } - } - - throw new RuntimeException("no default value: " + classname + "." - + name + "()"); - } - - /** - * Returns a hash code value for this object. - */ - public int hashCode() { - if (cachedHashCode == Integer.MIN_VALUE) { - int hashCode = 0; - - // Load the annotation class - getAnnotationType(); - - Method[] methods = annotationType.getDeclaredMethods(); - for (int i = 0; i < methods.length; ++ i) { - String name = methods[i].getName(); - int valueHashCode = 0; - - // Get the value - MemberValue mv = annotation.getMemberValue(name); - Object value = null; - try { - if (mv != null) - value = mv.getValue(classLoader, pool, methods[i]); - if (value == null) - value = getDefault(name, methods[i]); - } - catch (RuntimeException e) { - throw e; - } - catch (Exception e) { - throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e); - } - - // Calculate the hash code - if (value != null) { - if (value.getClass().isArray()) - valueHashCode = arrayHashCode(value); - else - valueHashCode = value.hashCode(); - } - hashCode += 127 * name.hashCode() ^ valueHashCode; - } - - cachedHashCode = hashCode; - } - return cachedHashCode; - } - - /** - * Check that another annotation equals ourselves. - * - * @param obj the other annotation - * @return the true when equals false otherwise - * @throws Exception for any problem - */ - private boolean checkEquals(Object obj) throws Exception { - if (obj == null) - return false; - - // Optimization when the other is one of ourselves - if (obj instanceof Proxy) { - InvocationHandler ih = Proxy.getInvocationHandler(obj); - if (ih instanceof AnnotationImpl) { - AnnotationImpl other = (AnnotationImpl) ih; - return annotation.equals(other.annotation); - } - } - - Class otherAnnotationType = (Class) JDK_ANNOTATION_TYPE_METHOD.invoke(obj, (Object[])null); - if (getAnnotationType().equals(otherAnnotationType) == false) - return false; - - Method[] methods = annotationType.getDeclaredMethods(); - for (int i = 0; i < methods.length; ++ i) { - String name = methods[i].getName(); - - // Get the value - MemberValue mv = annotation.getMemberValue(name); - Object value = null; - Object otherValue = null; - try { - if (mv != null) - value = mv.getValue(classLoader, pool, methods[i]); - if (value == null) - value = getDefault(name, methods[i]); - otherValue = methods[i].invoke(obj, (Object[])null); - } - catch (RuntimeException e) { - throw e; - } - catch (Exception e) { - throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e); - } - - if (value == null && otherValue != null) - return false; - if (value != null && value.equals(otherValue) == false) - return false; - } - - return true; - } - - /** - * Calculates the hashCode of an array using the same - * algorithm as java.util.Arrays.hashCode() - * - * @param object the object - * @return the hashCode - */ - private static int arrayHashCode(Object object) - { - if (object == null) - return 0; - - int result = 1; - - Object[] array = (Object[]) object; - for (int i = 0; i < array.length; ++i) { - int elementHashCode = 0; - if (array[i] != null) - elementHashCode = array[i].hashCode(); - result = 31 * result + elementHashCode; - } - return result; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java deleted file mode 100644 index 09932ec..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Nested annotation. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class AnnotationMemberValue extends MemberValue { - Annotation value; - - /** - * Constructs an annotation member. The initial value is not specified. - */ - public AnnotationMemberValue(ConstPool cp) { - this(null, cp); - } - - /** - * Constructs an annotation member. The initial value is specified by - * the first parameter. - */ - public AnnotationMemberValue(Annotation a, ConstPool cp) { - super('@', cp); - value = a; - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) - throws ClassNotFoundException - { - return AnnotationImpl.make(cl, getType(cl), cp, value); - } - - Class getType(ClassLoader cl) throws ClassNotFoundException { - if (value == null) - throw new ClassNotFoundException("no type specified"); - else - return loadClass(cl, value.getTypeName()); - } - - /** - * Obtains the value. - */ - public Annotation getValue() { - return value; - } - - /** - * Sets the value of this member. - */ - public void setValue(Annotation newValue) { - value = newValue; - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return value.toString(); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.annotationValue(); - value.write(writer); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitAnnotationMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java deleted file mode 100644 index 33beb2d..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import java.io.*; - -import com.wenshuo.agent.javassist.bytecode.ByteArray; -import com.wenshuo.agent.javassist.bytecode.ConstPool; - -/** - * A convenience class for constructing a - * ..Annotations_attribute. - * See the source code of the AnnotationsAttribute.Copier class. - * - *

The following code snippet is an example of use of this class: - * - *

- * ConstPool pool = ...;
- * output = new ByteArrayOutputStream();
- * writer = new AnnotationsWriter(output, pool);
- *
- * writer.numAnnotations(1);
- * writer.annotation("Author", 2);
- * writer.memberValuePair("name");      // element_value_pair
- * writer.constValueIndex("chiba");
- * writer.memberValuePair("address");   // element_value_pair
- * writer.constValueIndex("tokyo");
- *
- * writer.close();
- * byte[] attribute_info = output.toByteArray();
- * AnnotationsAttribute anno
- *     = new AnnotationsAttribute(pool, AnnotationsAttribute.visibleTag,
- *                                attribute_info);
- * 
- * - *

The code snippet above generates the annotation attribute - * corresponding to this annotation: - * - *

- *  @Author(name = "chiba", address = "tokyo")
- * 
- * - * @see javassist.bytecode.AnnotationsAttribute - * @see javassist.bytecode.ParameterAnnotationsAttribute - */ -public class AnnotationsWriter { - protected OutputStream output; - private ConstPool pool; - - /** - * Constructs with the given output stream. - * - * @param os the output stream. - * @param cp the constant pool. - */ - public AnnotationsWriter(OutputStream os, ConstPool cp) { - output = os; - pool = cp; - } - - /** - * Obtains the constant pool given to the constructor. - */ - public ConstPool getConstPool() { - return pool; - } - - /** - * Closes the output stream. - * - */ - public void close() throws IOException { - output.close(); - } - - /** - * Writes num_parameters in - * Runtime(In)VisibleParameterAnnotations_attribute. - * This method must be followed by num calls to - * numAnnotations(). - */ - public void numParameters(int num) throws IOException { - output.write(num); - } - - /** - * Writes num_annotations in - * Runtime(In)VisibleAnnotations_attribute. - * This method must be followed by num calls to - * annotation(). - */ - public void numAnnotations(int num) throws IOException { - write16bit(num); - } - - /** - * Writes annotation. - * This method must be followed by numMemberValuePairs - * calls to memberValuePair(). - * - * @param type the annotation interface name. - * @param numMemberValuePairs num_element_value_pairs - * in annotation. - */ - public void annotation(String type, int numMemberValuePairs) - throws IOException - { - annotation(pool.addUtf8Info(type), numMemberValuePairs); - } - - /** - * Writes annotation. - * This method must be followed by numMemberValuePairs - * calls to memberValuePair(). - * - * @param typeIndex type_index in annotation. - * @param numMemberValuePairs num_element_value_pairs - * in annotation. - */ - public void annotation(int typeIndex, int numMemberValuePairs) - throws IOException - { - write16bit(typeIndex); - write16bit(numMemberValuePairs); - } - - /** - * Writes an element of a element_value_pairs array - * in annotation. - * This method must be followed by a - * call to constValueIndex(), enumConstValue(), - * etc. - * - * @param memberName the element name. - */ - public void memberValuePair(String memberName) throws IOException { - memberValuePair(pool.addUtf8Info(memberName)); - } - - /** - * Writes an element of a element_value_pairs array - * in annotation. - * This method must be followed by a - * call to constValueIndex(), enumConstValue(), - * etc. - * - * @param memberNameIndex element_name_index - * in element_value_pairs array. - */ - public void memberValuePair(int memberNameIndex) throws IOException { - write16bit(memberNameIndex); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param value the constant value. - */ - public void constValueIndex(boolean value) throws IOException { - constValueIndex('Z', pool.addIntegerInfo(value ? 1 : 0)); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param value the constant value. - */ - public void constValueIndex(byte value) throws IOException { - constValueIndex('B', pool.addIntegerInfo(value)); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param value the constant value. - */ - public void constValueIndex(char value) throws IOException { - constValueIndex('C', pool.addIntegerInfo(value)); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param value the constant value. - */ - public void constValueIndex(short value) throws IOException { - constValueIndex('S', pool.addIntegerInfo(value)); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param value the constant value. - */ - public void constValueIndex(int value) throws IOException { - constValueIndex('I', pool.addIntegerInfo(value)); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param value the constant value. - */ - public void constValueIndex(long value) throws IOException { - constValueIndex('J', pool.addLongInfo(value)); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param value the constant value. - */ - public void constValueIndex(float value) throws IOException { - constValueIndex('F', pool.addFloatInfo(value)); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param value the constant value. - */ - public void constValueIndex(double value) throws IOException { - constValueIndex('D', pool.addDoubleInfo(value)); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param value the constant value. - */ - public void constValueIndex(String value) throws IOException { - constValueIndex('s', pool.addUtf8Info(value)); - } - - /** - * Writes tag and const_value_index - * in element_value. - * - * @param tag tag in element_value. - * @param index const_value_index - * in element_value. - */ - public void constValueIndex(int tag, int index) - throws IOException - { - output.write(tag); - write16bit(index); - } - - /** - * Writes tag and enum_const_value - * in element_value. - * - * @param typeName the type name of the enum constant. - * @param constName the simple name of the enum constant. - */ - public void enumConstValue(String typeName, String constName) - throws IOException - { - enumConstValue(pool.addUtf8Info(typeName), - pool.addUtf8Info(constName)); - } - - /** - * Writes tag and enum_const_value - * in element_value. - * - * @param typeNameIndex type_name_index - * in element_value. - * @param constNameIndex const_name_index - * in element_value. - */ - public void enumConstValue(int typeNameIndex, int constNameIndex) - throws IOException - { - output.write('e'); - write16bit(typeNameIndex); - write16bit(constNameIndex); - } - - /** - * Writes tag and class_info_index - * in element_value. - * - * @param name the class name. - */ - public void classInfoIndex(String name) throws IOException { - classInfoIndex(pool.addUtf8Info(name)); - } - - /** - * Writes tag and class_info_index - * in element_value. - * - * @param index class_info_index - */ - public void classInfoIndex(int index) throws IOException { - output.write('c'); - write16bit(index); - } - - /** - * Writes tag and annotation_value - * in element_value. - * This method must be followed by a call to annotation(). - */ - public void annotationValue() throws IOException { - output.write('@'); - } - - /** - * Writes tag and array_value - * in element_value. - * This method must be followed by numValues calls - * to constValueIndex(), enumConstValue(), - * etc. - * - * @param numValues num_values - * in array_value. - */ - public void arrayValue(int numValues) throws IOException { - output.write('['); - write16bit(numValues); - } - - protected void write16bit(int value) throws IOException { - byte[] buf = new byte[2]; - ByteArray.write16bit(value, buf, 0); - output.write(buf); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java deleted file mode 100644 index ff0ab0e..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Array; -import java.lang.reflect.Method; - -/** - * Array member. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class ArrayMemberValue extends MemberValue { - MemberValue type; - MemberValue[] values; - - /** - * Constructs an array. The initial value or type are not specified. - */ - public ArrayMemberValue(ConstPool cp) { - super('[', cp); - type = null; - values = null; - } - - /** - * Constructs an array. The initial value is not specified. - * - * @param t the type of the array elements. - */ - public ArrayMemberValue(MemberValue t, ConstPool cp) { - super('[', cp); - type = t; - values = null; - } - - Object getValue(ClassLoader cl, ClassPool cp, Method method) - throws ClassNotFoundException - { - if (values == null) - throw new ClassNotFoundException( - "no array elements found: " + method.getName()); - - int size = values.length; - Class clazz; - if (type == null) { - clazz = method.getReturnType().getComponentType(); - if (clazz == null || size > 0) - throw new ClassNotFoundException("broken array type: " - + method.getName()); - } - else - clazz = type.getType(cl); - - Object a = Array.newInstance(clazz, size); - for (int i = 0; i < size; i++) - Array.set(a, i, values[i].getValue(cl, cp, method)); - - return a; - } - - Class getType(ClassLoader cl) throws ClassNotFoundException { - if (type == null) - throw new ClassNotFoundException("no array type specified"); - - Object a = Array.newInstance(type.getType(cl), 0); - return a.getClass(); - } - - /** - * Obtains the type of the elements. - * - * @return null if the type is not specified. - */ - public MemberValue getType() { - return type; - } - - /** - * Obtains the elements of the array. - */ - public MemberValue[] getValue() { - return values; - } - - /** - * Sets the elements of the array. - */ - public void setValue(MemberValue[] elements) { - values = elements; - if (elements != null && elements.length > 0) - type = elements[0]; - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - StringBuffer buf = new StringBuffer("{"); - if (values != null) { - for (int i = 0; i < values.length; i++) { - buf.append(values[i].toString()); - if (i + 1 < values.length) - buf.append(", "); - } - } - - buf.append("}"); - return buf.toString(); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - int num = values.length; - writer.arrayValue(num); - for (int i = 0; i < num; ++i) - values[i].write(writer); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitArrayMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java deleted file mode 100644 index c03c2bb..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Boolean constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class BooleanMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs a boolean constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Integer_info structure. - */ - public BooleanMemberValue(int index, ConstPool cp) { - super('Z', cp); - this.valueIndex = index; - } - - /** - * Constructs a boolean constant value. - * - * @param b the initial value. - */ - public BooleanMemberValue(boolean b, ConstPool cp) { - super('Z', cp); - setValue(b); - } - - /** - * Constructs a boolean constant value. The initial value is false. - */ - public BooleanMemberValue(ConstPool cp) { - super('Z', cp); - setValue(false); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) { - return new Boolean(getValue()); - } - - Class getType(ClassLoader cl) { - return boolean.class; - } - - /** - * Obtains the value of the member. - */ - public boolean getValue() { - return cp.getIntegerInfo(valueIndex) != 0; - } - - /** - * Sets the value of the member. - */ - public void setValue(boolean newValue) { - valueIndex = cp.addIntegerInfo(newValue ? 1 : 0); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return getValue() ? "true" : "false"; - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.constValueIndex(getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitBooleanMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java deleted file mode 100644 index c26d182..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Byte constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class ByteMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs a byte constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Integer_info structure. - */ - public ByteMemberValue(int index, ConstPool cp) { - super('B', cp); - this.valueIndex = index; - } - - /** - * Constructs a byte constant value. - * - * @param b the initial value. - */ - public ByteMemberValue(byte b, ConstPool cp) { - super('B', cp); - setValue(b); - } - - /** - * Constructs a byte constant value. The initial value is 0. - */ - public ByteMemberValue(ConstPool cp) { - super('B', cp); - setValue((byte)0); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) { - return new Byte(getValue()); - } - - Class getType(ClassLoader cl) { - return byte.class; - } - - /** - * Obtains the value of the member. - */ - public byte getValue() { - return (byte)cp.getIntegerInfo(valueIndex); - } - - /** - * Sets the value of the member. - */ - public void setValue(byte newValue) { - valueIndex = cp.addIntegerInfo(newValue); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return Byte.toString(getValue()); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.constValueIndex(getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitByteMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java deleted file mode 100644 index 9fe9c4a..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Char constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class CharMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs a char constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Integer_info structure. - */ - public CharMemberValue(int index, ConstPool cp) { - super('C', cp); - this.valueIndex = index; - } - - /** - * Constructs a char constant value. - * - * @param c the initial value. - */ - public CharMemberValue(char c, ConstPool cp) { - super('C', cp); - setValue(c); - } - - /** - * Constructs a char constant value. The initial value is '\0'. - */ - public CharMemberValue(ConstPool cp) { - super('C', cp); - setValue('\0'); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) { - return new Character(getValue()); - } - - Class getType(ClassLoader cl) { - return char.class; - } - - /** - * Obtains the value of the member. - */ - public char getValue() { - return (char)cp.getIntegerInfo(valueIndex); - } - - /** - * Sets the value of the member. - */ - public void setValue(char newValue) { - valueIndex = cp.addIntegerInfo(newValue); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return Character.toString(getValue()); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.constValueIndex(getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitCharMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java deleted file mode 100644 index 4b3a697..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.bytecode.SignatureAttribute; - -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Class value. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class ClassMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs a class value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Utf8_info structure. - */ - public ClassMemberValue(int index, ConstPool cp) { - super('c', cp); - this.valueIndex = index; - } - - /** - * Constructs a class value. - * - * @param className the initial value. - */ - public ClassMemberValue(String className, ConstPool cp) { - super('c', cp); - setValue(className); - } - - /** - * Constructs a class value. - * The initial value is java.lang.Class. - */ - public ClassMemberValue(ConstPool cp) { - super('c', cp); - setValue("java.lang.Class"); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) - throws ClassNotFoundException { - final String classname = getValue(); - if (classname.equals("void")) - return void.class; - else if (classname.equals("int")) - return int.class; - else if (classname.equals("byte")) - return byte.class; - else if (classname.equals("long")) - return long.class; - else if (classname.equals("double")) - return double.class; - else if (classname.equals("float")) - return float.class; - else if (classname.equals("char")) - return char.class; - else if (classname.equals("short")) - return short.class; - else if (classname.equals("boolean")) - return boolean.class; - else - return loadClass(cl, classname); - } - - Class getType(ClassLoader cl) throws ClassNotFoundException { - return loadClass(cl, "java.lang.Class"); - } - - /** - * Obtains the value of the member. - * - * @return fully-qualified class name. - */ - public String getValue() { - String v = cp.getUtf8Info(valueIndex); - try { - return SignatureAttribute.toTypeSignature(v).jvmTypeName(); - } catch (BadBytecode e) { - throw new RuntimeException(e); - } - } - - /** - * Sets the value of the member. - * - * @param newClassName fully-qualified class name. - */ - public void setValue(String newClassName) { - String setTo = Descriptor.of(newClassName); - valueIndex = cp.addUtf8Info(setTo); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return getValue().replace('$', '.') + ".class"; - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.classInfoIndex(cp.getUtf8Info(valueIndex)); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitClassMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java deleted file mode 100644 index fcfdad0..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Double floating-point number constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - * @version $Revision: 1.7 $ - */ -public class DoubleMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs a double constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Double_info structure. - */ - public DoubleMemberValue(int index, ConstPool cp) { - super('D', cp); - this.valueIndex = index; - } - - /** - * Constructs a double constant value. - * - * @param d the initial value. - */ - public DoubleMemberValue(double d, ConstPool cp) { - super('D', cp); - setValue(d); - } - - /** - * Constructs a double constant value. The initial value is 0.0. - */ - public DoubleMemberValue(ConstPool cp) { - super('D', cp); - setValue(0.0); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) { - return new Double(getValue()); - } - - Class getType(ClassLoader cl) { - return double.class; - } - - /** - * Obtains the value of the member. - */ - public double getValue() { - return cp.getDoubleInfo(valueIndex); - } - - /** - * Sets the value of the member. - */ - public void setValue(double newValue) { - valueIndex = cp.addDoubleInfo(newValue); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return Double.toString(getValue()); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.constValueIndex(getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitDoubleMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java deleted file mode 100644 index fca4034..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import java.io.IOException; -import java.lang.reflect.Method; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; - -/** - * Enum constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class EnumMemberValue extends MemberValue { - int typeIndex, valueIndex; - - /** - * Constructs an enum constant value. The initial value is specified - * by the constant pool entries at the given indexes. - * - * @param type the index of a CONSTANT_Utf8_info structure - * representing the enum type. - * @param value the index of a CONSTANT_Utf8_info structure. - * representing the enum value. - */ - public EnumMemberValue(int type, int value, ConstPool cp) { - super('e', cp); - this.typeIndex = type; - this.valueIndex = value; - } - - /** - * Constructs an enum constant value. - * The initial value is not specified. - */ - public EnumMemberValue(ConstPool cp) { - super('e', cp); - typeIndex = valueIndex = 0; - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) - throws ClassNotFoundException - { - try { - return getType(cl).getField(getValue()).get(null); - } - catch (NoSuchFieldException e) { - throw new ClassNotFoundException(getType() + "." + getValue()); - } - catch (IllegalAccessException e) { - throw new ClassNotFoundException(getType() + "." + getValue()); - } - } - - Class getType(ClassLoader cl) throws ClassNotFoundException { - return loadClass(cl, getType()); - } - - /** - * Obtains the enum type name. - * - * @return a fully-qualified type name. - */ - public String getType() { - return Descriptor.toClassName(cp.getUtf8Info(typeIndex)); - } - - /** - * Changes the enum type name. - * - * @param typename a fully-qualified type name. - */ - public void setType(String typename) { - typeIndex = cp.addUtf8Info(Descriptor.of(typename)); - } - - /** - * Obtains the name of the enum constant value. - */ - public String getValue() { - return cp.getUtf8Info(valueIndex); - } - - /** - * Changes the name of the enum constant value. - */ - public void setValue(String name) { - valueIndex = cp.addUtf8Info(name); - } - - public String toString() { - return getType() + "." + getValue(); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.enumConstValue(cp.getUtf8Info(typeIndex), getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitEnumMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java deleted file mode 100644 index abb5095..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Floating-point number constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - * @version $Revision: 1.7 $ - */ -public class FloatMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs a float constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Float_info structure. - */ - public FloatMemberValue(int index, ConstPool cp) { - super('F', cp); - this.valueIndex = index; - } - - /** - * Constructs a float constant value. - * - * @param f the initial value. - */ - public FloatMemberValue(float f, ConstPool cp) { - super('F', cp); - setValue(f); - } - - /** - * Constructs a float constant value. The initial value is 0.0. - */ - public FloatMemberValue(ConstPool cp) { - super('F', cp); - setValue(0.0F); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) { - return new Float(getValue()); - } - - Class getType(ClassLoader cl) { - return float.class; - } - - /** - * Obtains the value of the member. - */ - public float getValue() { - return cp.getFloatInfo(valueIndex); - } - - /** - * Sets the value of the member. - */ - public void setValue(float newValue) { - valueIndex = cp.addFloatInfo(newValue); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return Float.toString(getValue()); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.constValueIndex(getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitFloatMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java deleted file mode 100644 index a112e4c..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Integer constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class IntegerMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs an int constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Integer_info structure. - */ - public IntegerMemberValue(int index, ConstPool cp) { - super('I', cp); - this.valueIndex = index; - } - - /** - * Constructs an int constant value. - * Note that this constructor receives the initial value - * as the second parameter - * unlike the corresponding constructors in the sibling classes. - * This is for making a difference from the constructor that receives - * an index into the constant pool table as the first parameter. - * Note that the index is also int type. - * - * @param value the initial value. - */ - public IntegerMemberValue(ConstPool cp, int value) { - super('I', cp); - setValue(value); - } - - /** - * Constructs an int constant value. The initial value is 0. - */ - public IntegerMemberValue(ConstPool cp) { - super('I', cp); - setValue(0); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) { - return new Integer(getValue()); - } - - Class getType(ClassLoader cl) { - return int.class; - } - - /** - * Obtains the value of the member. - */ - public int getValue() { - return cp.getIntegerInfo(valueIndex); - } - - /** - * Sets the value of the member. - */ - public void setValue(int newValue) { - valueIndex = cp.addIntegerInfo(newValue); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return Integer.toString(getValue()); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.constValueIndex(getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitIntegerMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java deleted file mode 100644 index 9be1876..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Long integer constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class LongMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs a long constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Long_info structure. - */ - public LongMemberValue(int index, ConstPool cp) { - super('J', cp); - this.valueIndex = index; - } - - /** - * Constructs a long constant value. - * - * @param j the initial value. - */ - public LongMemberValue(long j, ConstPool cp) { - super('J', cp); - setValue(j); - } - - /** - * Constructs a long constant value. The initial value is 0. - */ - public LongMemberValue(ConstPool cp) { - super('J', cp); - setValue(0L); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) { - return new Long(getValue()); - } - - Class getType(ClassLoader cl) { - return long.class; - } - - /** - * Obtains the value of the member. - */ - public long getValue() { - return cp.getLongInfo(valueIndex); - } - - /** - * Sets the value of the member. - */ - public void setValue(long newValue) { - valueIndex = cp.addLongInfo(newValue); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return Long.toString(getValue()); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.constValueIndex(getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitLongMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java deleted file mode 100644 index fcbd86f..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; - -import java.io.IOException; -import java.lang.reflect.Array; -import java.lang.reflect.Method; - -/** - * The value of a member declared in an annotation. - * - * @see Annotation#getMemberValue(String) - * @author Bill Burke - * @author Shigeru Chiba - */ -public abstract class MemberValue { - ConstPool cp; - char tag; - - MemberValue(char tag, ConstPool cp) { - this.cp = cp; - this.tag = tag; - } - - /** - * Returns the value. If the value type is a primitive type, the - * returned value is boxed. - */ - abstract Object getValue(ClassLoader cl, ClassPool cp, Method m) - throws ClassNotFoundException; - - abstract Class getType(ClassLoader cl) throws ClassNotFoundException; - - static Class loadClass(ClassLoader cl, String classname) - throws ClassNotFoundException, NoSuchClassError - { - try { - return Class.forName(convertFromArray(classname), true, cl); - } - catch (LinkageError e) { - throw new NoSuchClassError(classname, e); - } - } - - private static String convertFromArray(String classname) - { - int index = classname.indexOf("[]"); - if (index != -1) { - String rawType = classname.substring(0, index); - StringBuffer sb = new StringBuffer(Descriptor.of(rawType)); - while (index != -1) { - sb.insert(0, "["); - index = classname.indexOf("[]", index + 1); - } - return sb.toString().replace('/', '.'); - } - return classname; - } - - /** - * Accepts a visitor. - */ - public abstract void accept(MemberValueVisitor visitor); - - /** - * Writes the value. - */ - public abstract void write(AnnotationsWriter w) throws IOException; -} - - diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java deleted file mode 100644 index e20d31e..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -/** - * Visitor for traversing member values included in an annotation. - * - * @see MemberValue#accept(MemberValueVisitor) - * @author Bill Burke - */ -public interface MemberValueVisitor { - public void visitAnnotationMemberValue(AnnotationMemberValue node); - public void visitArrayMemberValue(ArrayMemberValue node); - public void visitBooleanMemberValue(BooleanMemberValue node); - public void visitByteMemberValue(ByteMemberValue node); - public void visitCharMemberValue(CharMemberValue node); - public void visitDoubleMemberValue(DoubleMemberValue node); - public void visitEnumMemberValue(EnumMemberValue node); - public void visitFloatMemberValue(FloatMemberValue node); - public void visitIntegerMemberValue(IntegerMemberValue node); - public void visitLongMemberValue(LongMemberValue node); - public void visitShortMemberValue(ShortMemberValue node); - public void visitStringMemberValue(StringMemberValue node); - public void visitClassMemberValue(ClassMemberValue node); -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java deleted file mode 100644 index 9b99786..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -/** - * Thrown if the linkage fails. - * It keeps the name of the class that caused this error. - */ -public class NoSuchClassError extends Error { - private String className; - - /** - * Constructs an exception. - */ - public NoSuchClassError(String className, Error cause) { - super(cause.toString(), cause); - this.className = className; - } - - /** - * Returns the name of the class not found. - */ - public String getClassName() { - return className; - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java deleted file mode 100644 index 8dd4970..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * Short integer constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class ShortMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs a short constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Integer_info structure. - */ - public ShortMemberValue(int index, ConstPool cp) { - super('S', cp); - this.valueIndex = index; - } - - /** - * Constructs a short constant value. - * - * @param s the initial value. - */ - public ShortMemberValue(short s, ConstPool cp) { - super('S', cp); - setValue(s); - } - - /** - * Constructs a short constant value. The initial value is 0. - */ - public ShortMemberValue(ConstPool cp) { - super('S', cp); - setValue((short)0); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) { - return new Short(getValue()); - } - - Class getType(ClassLoader cl) { - return short.class; - } - - /** - * Obtains the value of the member. - */ - public short getValue() { - return (short)cp.getIntegerInfo(valueIndex); - } - - /** - * Sets the value of the member. - */ - public void setValue(short newValue) { - valueIndex = cp.addIntegerInfo(newValue); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return Short.toString(getValue()); - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.constValueIndex(getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitShortMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java deleted file mode 100644 index 61ac276..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 2004 Bill Burke. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.annotation; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import java.io.IOException; -import java.lang.reflect.Method; - -/** - * String constant value. - * - * @author Bill Burke - * @author Shigeru Chiba - */ -public class StringMemberValue extends MemberValue { - int valueIndex; - - /** - * Constructs a string constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Utf8_info structure. - */ - public StringMemberValue(int index, ConstPool cp) { - super('s', cp); - this.valueIndex = index; - } - - /** - * Constructs a string constant value. - * - * @param str the initial value. - */ - public StringMemberValue(String str, ConstPool cp) { - super('s', cp); - setValue(str); - } - - /** - * Constructs a string constant value. The initial value is "". - */ - public StringMemberValue(ConstPool cp) { - super('s', cp); - setValue(""); - } - - Object getValue(ClassLoader cl, ClassPool cp, Method m) { - return getValue(); - } - - Class getType(ClassLoader cl) { - return String.class; - } - - /** - * Obtains the value of the member. - */ - public String getValue() { - return cp.getUtf8Info(valueIndex); - } - - /** - * Sets the value of the member. - */ - public void setValue(String newValue) { - valueIndex = cp.addUtf8Info(newValue); - } - - /** - * Obtains the string representation of this object. - */ - public String toString() { - return "\"" + getValue() + "\""; - } - - /** - * Writes the value. - */ - public void write(AnnotationsWriter writer) throws IOException { - writer.constValueIndex(getValue()); - } - - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) { - visitor.visitStringMemberValue(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java b/src/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java deleted file mode 100644 index c35185e..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.wenshuo.agent.javassist.bytecode.annotation; - -import java.io.IOException; -import java.io.OutputStream; - -import com.wenshuo.agent.javassist.bytecode.ConstPool; - -/** - * A convenience class for constructing a - * {@code ..TypeAnnotations_attribute}. - * See the source code of the {@link javassist.bytecode.TypeAnnotationsAttribute} class. - * - * @since 3.19 - */ -public class TypeAnnotationsWriter extends AnnotationsWriter { - /** - * Constructs with the given output stream. - * - * @param os the output stream. - * @param cp the constant pool. - */ - public TypeAnnotationsWriter(OutputStream os, ConstPool cp) { - super(os, cp); - } - - /** - * Writes {@code num_annotations} in - * {@code Runtime(In)VisibleTypeAnnotations_attribute}. - * It must be followed by {@code num} instances of {@code type_annotation}. - */ - public void numAnnotations(int num) throws IOException { - super.numAnnotations(num); - } - - /** - * Writes {@code target_type} and {@code type_parameter_target} - * of {@code target_info} union. - */ - public void typeParameterTarget(int targetType, int typeParameterIndex) - throws IOException - { - output.write(targetType); - output.write(typeParameterIndex); - } - - /** - * Writes {@code target_type} and {@code supertype_target} - * of {@code target_info} union. - */ - public void supertypeTarget(int supertypeIndex) - throws IOException - { - output.write(0x10); - write16bit(supertypeIndex); - } - - /** - * Writes {@code target_type} and {@code type_parameter_bound_target} - * of {@code target_info} union. - */ - public void typeParameterBoundTarget(int targetType, int typeParameterIndex, int boundIndex) - throws IOException - { - output.write(targetType); - output.write(typeParameterIndex); - output.write(boundIndex); - } - - /** - * Writes {@code target_type} and {@code empty_target} - * of {@code target_info} union. - */ - public void emptyTarget(int targetType) throws IOException { - output.write(targetType); - } - - /** - * Writes {@code target_type} and {@code type_parameter_target} - * of {@code target_info} union. - */ - public void formalParameterTarget(int formalParameterIndex) - throws IOException - { - output.write(0x16); - output.write(formalParameterIndex); - } - - /** - * Writes {@code target_type} and {@code throws_target} - * of {@code target_info} union. - */ - public void throwsTarget(int throwsTypeIndex) - throws IOException - { - output.write(0x17); - write16bit(throwsTypeIndex); - } - - /** - * Writes {@code target_type} and {@code localvar_target} - * of {@code target_info} union. - * It must be followed by {@code tableLength} calls - * to {@code localVarTargetTable}. - */ - public void localVarTarget(int targetType, int tableLength) - throws IOException - { - output.write(targetType); - write16bit(tableLength); - } - - /** - * Writes an element of {@code table[]} of {@code localvar_target} - * of {@code target_info} union. - */ - public void localVarTargetTable(int startPc, int length, int index) - throws IOException - { - write16bit(startPc); - write16bit(length); - write16bit(index); - } - - /** - * Writes {@code target_type} and {@code catch_target} - * of {@code target_info} union. - */ - public void catchTarget(int exceptionTableIndex) - throws IOException - { - output.write(0x42); - write16bit(exceptionTableIndex); - } - - /** - * Writes {@code target_type} and {@code offset_target} - * of {@code target_info} union. - */ - public void offsetTarget(int targetType, int offset) - throws IOException - { - output.write(targetType); - write16bit(offset); - } - - /** - * Writes {@code target_type} and {@code type_argument_target} - * of {@code target_info} union. - */ - public void typeArgumentTarget(int targetType, int offset, int type_argument_index) - throws IOException - { - output.write(targetType); - write16bit(offset); - output.write(type_argument_index); - } - - /** - * Writes {@code path_length} of {@code type_path}. - */ - public void typePath(int pathLength) throws IOException { - output.write(pathLength); - } - - /** - * Writes an element of {@code path[]} of {@code type_path}. - */ - public void typePathPath(int typePathKind, int typeArgumentIndex) - throws IOException - { - output.write(typePathKind); - output.write(typeArgumentIndex); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/annotation/package.html b/src/com/wenshuo/agent/javassist/bytecode/annotation/package.html deleted file mode 100644 index d0656db..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/annotation/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - -Bytecode-level Annotations API. - -

This package provides low-level API for editing annotations attributes. - - - diff --git a/src/com/wenshuo/agent/javassist/bytecode/package.html b/src/com/wenshuo/agent/javassist/bytecode/package.html deleted file mode 100644 index 9da3888..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/package.html +++ /dev/null @@ -1,18 +0,0 @@ - - -Bytecode-level API. - -

This package provides low-level API for editing a raw class file. -It allows the users to read and modify a constant pool entry, a single -bytecode instruction, and so on. - -

The users of this package must know the specifications of -class file and Java bytecode. For more details, read this book: - -

    Tim Lindholm and Frank Yellin, -"The Java Virtual Machine Specification 2nd Ed.", -Addison-Wesley, 1999. -
- - - diff --git a/src/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java b/src/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java deleted file mode 100644 index f85821b..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.stackmap; - -import com.wenshuo.agent.javassist.bytecode.*; -import java.util.HashMap; -import java.util.ArrayList; - -/** - * A basic block is a sequence of bytecode that does not contain jump/branch - * instructions except at the last bytecode. - * Since Java7 or later does not allow JSR, this class throws an exception when - * it finds JSR. - */ -public class BasicBlock { - static class JsrBytecode extends BadBytecode { - JsrBytecode() { super("JSR"); } - } - - protected int position, length; - protected int incoming; // the number of incoming branches. - protected BasicBlock[] exit; // null if the block is a leaf. - protected boolean stop; // true if the block ends with an unconditional jump. - protected Catch toCatch; - - protected BasicBlock(int pos) { - position = pos; - length = 0; - incoming = 0; - } - - public static BasicBlock find(BasicBlock[] blocks, int pos) - throws BadBytecode - { - for (int i = 0; i < blocks.length; i++) { - int iPos = blocks[i].position; - if (iPos <= pos && pos < iPos + blocks[i].length) - return blocks[i]; - } - - throw new BadBytecode("no basic block at " + pos); - } - - public static class Catch { - public Catch next; - public BasicBlock body; - public int typeIndex; - Catch(BasicBlock b, int i, Catch c) { - body = b; - typeIndex = i; - next = c; - } - } - - public String toString() { - StringBuffer sbuf = new StringBuffer(); - String cname = this.getClass().getName(); - int i = cname.lastIndexOf('.'); - sbuf.append(i < 0 ? cname : cname.substring(i + 1)); - sbuf.append("["); - toString2(sbuf); - sbuf.append("]"); - return sbuf.toString(); - } - - protected void toString2(StringBuffer sbuf) { - sbuf.append("pos=").append(position).append(", len=") - .append(length).append(", in=").append(incoming) - .append(", exit{"); - if (exit != null) { - for (int i = 0; i < exit.length; i++) - sbuf.append(exit[i].position).append(","); - } - - sbuf.append("}, {"); - Catch th = toCatch; - while (th != null) { - sbuf.append("(").append(th.body.position).append(", ") - .append(th.typeIndex).append("), "); - th = th.next; - } - - sbuf.append("}"); - } - - /** - * A Mark indicates the position of a branch instruction - * or a branch target. - */ - static class Mark implements Comparable { - int position; - BasicBlock block; - BasicBlock[] jump; - boolean alwaysJmp; // true if an unconditional branch. - int size; // 0 unless the mark indicates RETURN etc. - Catch catcher; - - Mark(int p) { - position = p; - block = null; - jump = null; - alwaysJmp = false; - size = 0; - catcher = null; - } - - public int compareTo(Object obj) { - if (obj instanceof Mark) { - int pos = ((Mark)obj).position; - return position - pos; - } - - return -1; - } - - void setJump(BasicBlock[] bb, int s, boolean always) { - jump = bb; - size = s; - alwaysJmp = always; - } - } - - public static class Maker { - /* Override these two methods if a subclass of BasicBlock must be - * instantiated. - */ - protected BasicBlock makeBlock(int pos) { - return new BasicBlock(pos); - } - - protected BasicBlock[] makeArray(int size) { - return new BasicBlock[size]; - } - - private BasicBlock[] makeArray(BasicBlock b) { - BasicBlock[] array = makeArray(1); - array[0] = b; - return array; - } - - private BasicBlock[] makeArray(BasicBlock b1, BasicBlock b2) { - BasicBlock[] array = makeArray(2); - array[0] = b1; - array[1] = b2; - return array; - } - - public BasicBlock[] make(MethodInfo minfo) throws BadBytecode { - CodeAttribute ca = minfo.getCodeAttribute(); - if (ca == null) - return null; - - CodeIterator ci = ca.iterator(); - return make(ci, 0, ci.getCodeLength(), ca.getExceptionTable()); - } - - public BasicBlock[] make(CodeIterator ci, int begin, int end, - ExceptionTable et) - throws BadBytecode - { - HashMap marks = makeMarks(ci, begin, end, et); - BasicBlock[] bb = makeBlocks(marks); - addCatchers(bb, et); - return bb; - } - - /* Branch target - */ - private Mark makeMark(HashMap table, int pos) { - return makeMark0(table, pos, true, true); - } - - /* Branch instruction. - * size > 0 - */ - private Mark makeMark(HashMap table, int pos, BasicBlock[] jump, - int size, boolean always) { - Mark m = makeMark0(table, pos, false, false); - m.setJump(jump, size, always); - return m; - } - - private Mark makeMark0(HashMap table, int pos, - boolean isBlockBegin, boolean isTarget) { - Integer p = new Integer(pos); - Mark m = (Mark)table.get(p); - if (m == null) { - m = new Mark(pos); - table.put(p, m); - } - - if (isBlockBegin) { - if (m.block == null) - m.block = makeBlock(pos); - - if (isTarget) - m.block.incoming++; - } - - return m; - } - - private HashMap makeMarks(CodeIterator ci, int begin, int end, - ExceptionTable et) - throws BadBytecode - { - ci.begin(); - ci.move(begin); - HashMap marks = new HashMap(); - while (ci.hasNext()) { - int index = ci.next(); - if (index >= end) - break; - - int op = ci.byteAt(index); - if ((Opcode.IFEQ <= op && op <= Opcode.IF_ACMPNE) - || op == Opcode.IFNULL || op == Opcode.IFNONNULL) { - Mark to = makeMark(marks, index + ci.s16bitAt(index + 1)); - Mark next = makeMark(marks, index + 3); - makeMark(marks, index, makeArray(to.block, next.block), 3, false); - } - else if (Opcode.GOTO <= op && op <= Opcode.LOOKUPSWITCH) - switch (op) { - case Opcode.GOTO : - makeGoto(marks, index, index + ci.s16bitAt(index + 1), 3); - break; - case Opcode.JSR : - makeJsr(marks, index, index + ci.s16bitAt(index + 1), 3); - break; - case Opcode.RET : - makeMark(marks, index, null, 2, true); - break; - case Opcode.TABLESWITCH : { - int pos = (index & ~3) + 4; - int low = ci.s32bitAt(pos + 4); - int high = ci.s32bitAt(pos + 8); - int ncases = high - low + 1; - BasicBlock[] to = makeArray(ncases + 1); - to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target - int p = pos + 12; - int n = p + ncases * 4; - int k = 1; - while (p < n) { - to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block; - p += 4; - } - makeMark(marks, index, to, n - index, true); - break; } - case Opcode.LOOKUPSWITCH : { - int pos = (index & ~3) + 4; - int ncases = ci.s32bitAt(pos + 4); - BasicBlock[] to = makeArray(ncases + 1); - to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target - int p = pos + 8 + 4; - int n = p + ncases * 8 - 4; - int k = 1; - while (p < n) { - to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block; - p += 8; - } - makeMark(marks, index, to, n - index, true); - break; } - } - else if ((Opcode.IRETURN <= op && op <= Opcode.RETURN) || op == Opcode.ATHROW) - makeMark(marks, index, null, 1, true); - else if (op == Opcode.GOTO_W) - makeGoto(marks, index, index + ci.s32bitAt(index + 1), 5); - else if (op == Opcode.JSR_W) - makeJsr(marks, index, index + ci.s32bitAt(index + 1), 5); - else if (op == Opcode.WIDE && ci.byteAt(index + 1) == Opcode.RET) - makeMark(marks, index, null, 4, true); - } - - if (et != null) { - int i = et.size(); - while (--i >= 0) { - makeMark0(marks, et.startPc(i), true, false); - makeMark(marks, et.handlerPc(i)); - } - } - - return marks; - } - - private void makeGoto(HashMap marks, int pos, int target, int size) { - Mark to = makeMark(marks, target); - BasicBlock[] jumps = makeArray(to.block); - makeMark(marks, pos, jumps, size, true); - } - - /* - * We could ignore JSR since Java 7 or later does not allow it. - * See The JVM Spec. Sec. 4.10.2.5. - */ - protected void makeJsr(HashMap marks, int pos, int target, int size) throws BadBytecode { - /* - Mark to = makeMark(marks, target); - Mark next = makeMark(marks, pos + size); - BasicBlock[] jumps = makeArray(to.block, next.block); - makeMark(marks, pos, jumps, size, false); - */ - throw new JsrBytecode(); - } - - private BasicBlock[] makeBlocks(HashMap markTable) { - Mark[] marks = (Mark[])markTable.values() - .toArray(new Mark[markTable.size()]); - java.util.Arrays.sort(marks); - ArrayList blocks = new ArrayList(); - int i = 0; - BasicBlock prev; - if (marks.length > 0 && marks[0].position == 0 && marks[0].block != null) - prev = getBBlock(marks[i++]); - else - prev = makeBlock(0); - - blocks.add(prev); - while (i < marks.length) { - Mark m = marks[i++]; - BasicBlock bb = getBBlock(m); - if (bb == null) { - // the mark indicates a branch instruction - if (prev.length > 0) { - // the previous mark already has exits. - prev = makeBlock(prev.position + prev.length); - blocks.add(prev); - } - - prev.length = m.position + m.size - prev.position; - prev.exit = m.jump; - prev.stop = m.alwaysJmp; - } - else { - // the mark indicates a branch target - if (prev.length == 0) { - prev.length = m.position - prev.position; - bb.incoming++; - prev.exit = makeArray(bb); - } - else { - // the previous mark already has exits. - if (prev.position + prev.length < m.position) { - // dead code is found. - prev = makeBlock(prev.position + prev.length); - blocks.add(prev); - prev.length = m.position - prev.position; - // the incoming flow from dead code is not counted - // bb.incoming++; - prev.stop = true; // because the incoming flow is not counted. - prev.exit = makeArray(bb); - } - } - - blocks.add(bb); - prev = bb; - } - } - - return (BasicBlock[])blocks.toArray(makeArray(blocks.size())); - } - - private static BasicBlock getBBlock(Mark m) { - BasicBlock b = m.block; - if (b != null && m.size > 0) { - b.exit = m.jump; - b.length = m.size; - b.stop = m.alwaysJmp; - } - - return b; - } - - private void addCatchers(BasicBlock[] blocks, ExceptionTable et) - throws BadBytecode - { - if (et == null) - return; - - int i = et.size(); - while (--i >= 0) { - BasicBlock handler = find(blocks, et.handlerPc(i)); - int start = et.startPc(i); - int end = et.endPc(i); - int type = et.catchType(i); - handler.incoming--; - for (int k = 0; k < blocks.length; k++) { - BasicBlock bb = blocks[k]; - int iPos = bb.position; - if (start <= iPos && iPos < end) { - bb.toCatch = new Catch(handler, type, bb.toCatch); - handler.incoming++; - } - } - } - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java b/src/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java deleted file mode 100644 index 19f64d5..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.stackmap; - -import java.util.ArrayList; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.*; - -/** - * Stack map maker. - */ -public class MapMaker extends Tracer { - /* - public static void main(String[] args) throws Exception { - boolean useMain2 = args[0].equals("0"); - if (useMain2 && args.length > 1) { - main2(args); - return; - } - - for (int i = 0; i < args.length; i++) - main1(args[i]); - } - - public static void main1(String className) throws Exception { - ClassPool cp = ClassPool.getDefault(); - //javassist.CtClass cc = cp.get(className); - javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(className)); - System.out.println(className); - ClassFile cf = cc.getClassFile(); - java.util.List minfos = cf.getMethods(); - for (int i = 0; i < minfos.size(); i++) { - MethodInfo minfo = (MethodInfo)minfos.get(i); - CodeAttribute ca = minfo.getCodeAttribute(); - if (ca != null) - ca.setAttribute(make(cp, minfo)); - } - - cc.writeFile("tmp"); - } - - public static void main2(String[] args) throws Exception { - ClassPool cp = ClassPool.getDefault(); - //javassist.CtClass cc = cp.get(args[1]); - javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(args[1])); - MethodInfo minfo; - if (args[2].equals("_init_")) - minfo = cc.getDeclaredConstructors()[0].getMethodInfo(); - // minfo = cc.getClassInitializer().getMethodInfo(); - else - minfo = cc.getDeclaredMethod(args[2]).getMethodInfo(); - - CodeAttribute ca = minfo.getCodeAttribute(); - if (ca == null) { - System.out.println("abstarct method"); - return; - } - - TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, false); - MapMaker mm = new MapMaker(cp, minfo, ca); - mm.make(blocks, ca.getCode()); - for (int i = 0; i < blocks.length; i++) - System.out.println(blocks[i]); - } - */ - - /** - * Computes the stack map table of the given method and returns it. - * It returns null if the given method does not have to have a - * stack map table or it includes JSR. - */ - public static StackMapTable make(ClassPool classes, MethodInfo minfo) - throws BadBytecode - { - CodeAttribute ca = minfo.getCodeAttribute(); - if (ca == null) - return null; - - TypedBlock[] blocks; - try { - blocks = TypedBlock.makeBlocks(minfo, ca, true); - } - catch (BasicBlock.JsrBytecode e) { - return null; - } - - if (blocks == null) - return null; - - MapMaker mm = new MapMaker(classes, minfo, ca); - try { - mm.make(blocks, ca.getCode()); - } - catch (BadBytecode bb) { - throw new BadBytecode(minfo, bb); - } - - return mm.toStackMap(blocks); - } - - /** - * Computes the stack map table for J2ME. - * It returns null if the given method does not have to have a - * stack map table or it includes JSR. - */ - public static StackMap make2(ClassPool classes, MethodInfo minfo) - throws BadBytecode - { - CodeAttribute ca = minfo.getCodeAttribute(); - if (ca == null) - return null; - - TypedBlock[] blocks; - try { - blocks = TypedBlock.makeBlocks(minfo, ca, true); - } - catch (BasicBlock.JsrBytecode e) { - return null; - } - - if (blocks == null) - return null; - - MapMaker mm = new MapMaker(classes, minfo, ca); - try { - mm.make(blocks, ca.getCode()); - } - catch (BadBytecode bb) { - throw new BadBytecode(minfo, bb); - } - return mm.toStackMap2(minfo.getConstPool(), blocks); - } - - public MapMaker(ClassPool classes, MethodInfo minfo, CodeAttribute ca) { - super(classes, minfo.getConstPool(), - ca.getMaxStack(), ca.getMaxLocals(), - TypedBlock.getRetType(minfo.getDescriptor())); - } - - protected MapMaker(MapMaker old) { super(old); } - - /** - * Runs an analyzer (Phase 1 and 2). - */ - void make(TypedBlock[] blocks, byte[] code) - throws BadBytecode - { - make(code, blocks[0]); - findDeadCatchers(code, blocks); - try { - fixTypes(code, blocks); - } catch (NotFoundException e) { - throw new BadBytecode("failed to resolve types", e); - } - } - - // Phase 1 - - private void make(byte[] code, TypedBlock tb) - throws BadBytecode - { - copyTypeData(tb.stackTop, tb.stackTypes, stackTypes); - stackTop = tb.stackTop; - copyTypeData(tb.localsTypes.length, tb.localsTypes, localsTypes); - - traceException(code, tb.toCatch); - - int pos = tb.position; - int end = pos + tb.length; - while (pos < end) { - pos += doOpcode(pos, code); - traceException(code, tb.toCatch); - } - - if (tb.exit != null) { - for (int i = 0; i < tb.exit.length; i++) { - TypedBlock e = (TypedBlock)tb.exit[i]; - if (e.alreadySet()) - mergeMap(e, true); - else { - recordStackMap(e); - MapMaker maker = new MapMaker(this); - maker.make(code, e); - } - } - } - } - - private void traceException(byte[] code, TypedBlock.Catch handler) - throws BadBytecode - { - while (handler != null) { - TypedBlock tb = (TypedBlock)handler.body; - if (tb.alreadySet()) { - mergeMap(tb, false); - if (tb.stackTop < 1) - throw new BadBytecode("bad catch clause: " + handler.typeIndex); - - tb.stackTypes[0] = merge(toExceptionType(handler.typeIndex), - tb.stackTypes[0]); - } - else { - recordStackMap(tb, handler.typeIndex); - MapMaker maker = new MapMaker(this); - maker.make(code, tb); - } - - handler = handler.next; - } - } - - private void mergeMap(TypedBlock dest, boolean mergeStack) throws BadBytecode { - int n = localsTypes.length; - for (int i = 0; i < n; i++) - dest.localsTypes[i] = merge(validateTypeData(localsTypes, n, i), - dest.localsTypes[i]); - - if (mergeStack) { - n = stackTop; - for (int i = 0; i < n; i++) - dest.stackTypes[i] = merge(stackTypes[i], dest.stackTypes[i]); - } - } - - private TypeData merge(TypeData src, TypeData target) throws BadBytecode { - if (src == target) - return target; - else if (target instanceof TypeData.ClassName - || target instanceof TypeData.BasicType) // a parameter - return target; - else if (target instanceof TypeData.AbsTypeVar) { - ((TypeData.AbsTypeVar)target).merge(src); - return target; - } - else - throw new RuntimeException("fatal: this should never happen"); - } - - private void recordStackMap(TypedBlock target) - throws BadBytecode - { - TypeData[] tStackTypes = TypeData.make(stackTypes.length); - int st = stackTop; - recordTypeData(st, stackTypes, tStackTypes); - recordStackMap0(target, st, tStackTypes); - } - - private void recordStackMap(TypedBlock target, int exceptionType) - throws BadBytecode - { - TypeData[] tStackTypes = TypeData.make(stackTypes.length); - tStackTypes[0] = toExceptionType(exceptionType).join(); - recordStackMap0(target, 1, tStackTypes); - } - - private TypeData.ClassName toExceptionType(int exceptionType) { - String type; - if (exceptionType == 0) // for finally clauses - type= "java.lang.Throwable"; - else - type = cpool.getClassInfo(exceptionType); - - return new TypeData.ClassName(type); - } - - private void recordStackMap0(TypedBlock target, int st, TypeData[] tStackTypes) - throws BadBytecode - { - int n = localsTypes.length; - TypeData[] tLocalsTypes = TypeData.make(n); - int k = recordTypeData(n, localsTypes, tLocalsTypes); - target.setStackMap(st, tStackTypes, k, tLocalsTypes); - } - - protected static int recordTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) { - int k = -1; - for (int i = 0; i < n; i++) { - TypeData t = validateTypeData(srcTypes, n, i); - destTypes[i] = t.join(); - if (t != TOP) - k = i + 1; // t might be long or double. - } - - return k + 1; - } - - protected static void copyTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) { - for (int i = 0; i < n; i++) - destTypes[i] = srcTypes[i]; - } - - private static TypeData validateTypeData(TypeData[] data, int length, int index) { - TypeData td = data[index]; - if (td.is2WordType() && index + 1 < length) - if (data[index + 1] != TOP) - return TOP; - - return td; - } - - // Phase 1.5 - - /* - * Javac may generate an exception handler that catches only the exception - * thrown within the handler itself. It is dead code. - * See javassist.JvstTest4.testJIRA195(). - */ - - private void findDeadCatchers(byte[] code, TypedBlock[] blocks) throws BadBytecode { - int len = blocks.length; - for (int i = 0; i < len; i++) { - TypedBlock block = blocks[i]; - if (!block.alreadySet()) { - fixDeadcode(code, block); - BasicBlock.Catch handler = block.toCatch; - if (handler != null) { - TypedBlock tb = (TypedBlock)handler.body; - if (!tb.alreadySet()) { - // tb is a handler that catches only the exceptions - // thrown from dead code. - recordStackMap(tb, handler.typeIndex); - fixDeadcode(code, tb); - tb.incoming = 1; - } - } - - } - } - } - - private void fixDeadcode(byte[] code, TypedBlock block) throws BadBytecode { - int pos = block.position; - int len = block.length - 3; - if (len < 0) { - // if the dead-code length is shorter than 3 bytes. - if (len == -1) - code[pos] = Bytecode.NOP; - - code[pos + block.length - 1] = (byte)Bytecode.ATHROW; - block.incoming = 1; - recordStackMap(block, 0); - return; - } - - // if block.incomping > 0, all the incoming edges are from - // other dead code blocks. So set block.incoming to 0. - block.incoming = 0; - - for (int k = 0; k < len; k++) - code[pos + k] = Bytecode.NOP; - - code[pos + len] = (byte)Bytecode.GOTO; - ByteArray.write16bit(-len, code, pos + len + 1); - } - - // Phase 2 - - /* - * This method first finds strongly connected components (SCCs) - * in a TypeData graph by Tarjan's algorithm. - * SCCs are TypeData nodes sharing the same type. - * Since SCCs are found in the topologically sorted order, - * their types are also fixed when they are found. - */ - private void fixTypes(byte[] code, TypedBlock[] blocks) throws NotFoundException, BadBytecode { - ArrayList preOrder = new ArrayList(); - int len = blocks.length; - int index = 0; - for (int i = 0; i < len; i++) { - TypedBlock block = blocks[i]; - if (block.alreadySet()) { // if block is not dead code - int n = block.localsTypes.length; - for (int j = 0; j < n; j++) - index = block.localsTypes[j].dfs(preOrder, index, classPool); - - n = block.stackTop; - for (int j = 0; j < n; j++) - index = block.stackTypes[j].dfs(preOrder, index, classPool); - } - } - } - - // Phase 3 - - public StackMapTable toStackMap(TypedBlock[] blocks) { - StackMapTable.Writer writer = new StackMapTable.Writer(32); - int n = blocks.length; - TypedBlock prev = blocks[0]; - int offsetDelta = prev.length; - if (prev.incoming > 0) { // the first instruction is a branch target. - writer.sameFrame(0); - offsetDelta--; - } - - for (int i = 1; i < n; i++) { - TypedBlock bb = blocks[i]; - if (isTarget(bb, blocks[i - 1])) { - bb.resetNumLocals(); - int diffL = stackMapDiff(prev.numLocals, prev.localsTypes, - bb.numLocals, bb.localsTypes); - toStackMapBody(writer, bb, diffL, offsetDelta, prev); - offsetDelta = bb.length - 1; - prev = bb; - } - else if (bb.incoming == 0) { - // dead code. - writer.sameFrame(offsetDelta); - offsetDelta = bb.length - 1; - prev = bb; - } - else - offsetDelta += bb.length; - } - - return writer.toStackMapTable(cpool); - } - - /** - * Returns true if cur is a branch target. - */ - private boolean isTarget(TypedBlock cur, TypedBlock prev) { - int in = cur.incoming; - if (in > 1) - return true; - else if (in < 1) - return false; - - return prev.stop; - } - - private void toStackMapBody(StackMapTable.Writer writer, TypedBlock bb, - int diffL, int offsetDelta, TypedBlock prev) { - // if diffL is -100, two TypeData arrays do not share - // any elements. - - int stackTop = bb.stackTop; - if (stackTop == 0) { - if (diffL == 0) { - writer.sameFrame(offsetDelta); - return; - } - else if (0 > diffL && diffL >= -3) { - writer.chopFrame(offsetDelta, -diffL); - return; - } - else if (0 < diffL && diffL <= 3) { - int[] data = new int[diffL]; - int[] tags = fillStackMap(bb.numLocals - prev.numLocals, - prev.numLocals, data, - bb.localsTypes); - writer.appendFrame(offsetDelta, tags, data); - return; - } - } - else if (stackTop == 1 && diffL == 0) { - TypeData td = bb.stackTypes[0]; - writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool)); - return; - } - else if (stackTop == 2 && diffL == 0) { - TypeData td = bb.stackTypes[0]; - if (td.is2WordType()) { - // bb.stackTypes[1] must be TOP. - writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool)); - return; - } - } - - int[] sdata = new int[stackTop]; - int[] stags = fillStackMap(stackTop, 0, sdata, bb.stackTypes); - int[] ldata = new int[bb.numLocals]; - int[] ltags = fillStackMap(bb.numLocals, 0, ldata, bb.localsTypes); - writer.fullFrame(offsetDelta, ltags, ldata, stags, sdata); - } - - private int[] fillStackMap(int num, int offset, int[] data, TypeData[] types) { - int realNum = diffSize(types, offset, offset + num); - ConstPool cp = cpool; - int[] tags = new int[realNum]; - int j = 0; - for (int i = 0; i < num; i++) { - TypeData td = types[offset + i]; - tags[j] = td.getTypeTag(); - data[j] = td.getTypeData(cp); - if (td.is2WordType()) - i++; - - j++; - } - - return tags; - } - - private static int stackMapDiff(int oldTdLen, TypeData[] oldTd, - int newTdLen, TypeData[] newTd) - { - int diff = newTdLen - oldTdLen; - int len; - if (diff > 0) - len = oldTdLen; - else - len = newTdLen; - - if (stackMapEq(oldTd, newTd, len)) - if (diff > 0) - return diffSize(newTd, len, newTdLen); - else - return -diffSize(oldTd, len, oldTdLen); - else - return -100; - } - - private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) { - for (int i = 0; i < len; i++) { - if (!oldTd[i].eq(newTd[i])) - return false; - } - - return true; - } - - private static int diffSize(TypeData[] types, int offset, int len) { - int num = 0; - while (offset < len) { - TypeData td = types[offset++]; - num++; - if (td.is2WordType()) - offset++; - } - - return num; - } - - // Phase 3 for J2ME - - public StackMap toStackMap2(ConstPool cp, TypedBlock[] blocks) { - StackMap.Writer writer = new StackMap.Writer(); - int n = blocks.length; // should be > 0 - boolean[] effective = new boolean[n]; - TypedBlock prev = blocks[0]; - - // Is the first instruction a branch target? - effective[0] = prev.incoming > 0; - - int num = effective[0] ? 1 : 0; - for (int i = 1; i < n; i++) { - TypedBlock bb = blocks[i]; - if (effective[i] = isTarget(bb, blocks[i - 1])) { - bb.resetNumLocals(); - prev = bb; - num++; - } - } - - if (num == 0) - return null; - - writer.write16bit(num); - for (int i = 0; i < n; i++) - if (effective[i]) - writeStackFrame(writer, cp, blocks[i].position, blocks[i]); - - return writer.toStackMap(cp); - } - - private void writeStackFrame(StackMap.Writer writer, ConstPool cp, int offset, TypedBlock tb) { - writer.write16bit(offset); - writeVerifyTypeInfo(writer, cp, tb.localsTypes, tb.numLocals); - writeVerifyTypeInfo(writer, cp, tb.stackTypes, tb.stackTop); - } - - private void writeVerifyTypeInfo(StackMap.Writer writer, ConstPool cp, TypeData[] types, int num) { - int numDWord = 0; - for (int i = 0; i < num; i++) { - TypeData td = types[i]; - if (td != null && td.is2WordType()) { - numDWord++; - i++; - } - } - - writer.write16bit(num - numDWord); - for (int i = 0; i < num; i++) { - TypeData td = types[i]; - writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp)); - if (td.is2WordType()) - i++; - } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java b/src/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java deleted file mode 100644 index caef5c9..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java +++ /dev/null @@ -1,933 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.stackmap; - -import com.wenshuo.agent.javassist.bytecode.ByteArray; -import com.wenshuo.agent.javassist.bytecode.Opcode; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.ClassPool; - -/* - * A class for performing abstract interpretation. - * See also MapMaker class. - */ - -public abstract class Tracer implements TypeTag { - protected ClassPool classPool; - protected ConstPool cpool; - protected String returnType; // used as the type of ARETURN - - protected int stackTop; - protected TypeData[] stackTypes; - protected TypeData[] localsTypes; - - public Tracer(ClassPool classes, ConstPool cp, int maxStack, int maxLocals, - String retType) { - classPool = classes; - cpool = cp; - returnType = retType; - stackTop = 0; - stackTypes = TypeData.make(maxStack); - localsTypes = TypeData.make(maxLocals); - } - - public Tracer(Tracer t) { - classPool = t.classPool; - cpool = t.cpool; - returnType = t.returnType; - stackTop = t.stackTop; - stackTypes = TypeData.make(t.stackTypes.length); - localsTypes = TypeData.make(t.localsTypes.length); - } - - /** - * Does abstract interpretation on the given bytecode instruction. - * It records whether or not a local variable (i.e. register) is accessed. - * If the instruction requires that a local variable or - * a stack element has a more specific type, this method updates the - * type of it. - * - * @param pos the position of the instruction. - * @return the size of the instruction at POS. - */ - protected int doOpcode(int pos, byte[] code) throws BadBytecode { - try { - int op = code[pos] & 0xff; - if (op < 96) - if (op < 54) - return doOpcode0_53(pos, code, op); - else - return doOpcode54_95(pos, code, op); - else - if (op < 148) - return doOpcode96_147(pos, code, op); - else - return doOpcode148_201(pos, code, op); - } - catch (ArrayIndexOutOfBoundsException e) { - throw new BadBytecode("inconsistent stack height " + e.getMessage(), e); - } - } - - protected void visitBranch(int pos, byte[] code, int offset) throws BadBytecode {} - protected void visitGoto(int pos, byte[] code, int offset) throws BadBytecode {} - protected void visitReturn(int pos, byte[] code) throws BadBytecode {} - protected void visitThrow(int pos, byte[] code) throws BadBytecode {} - - /** - * @param pos the position of TABLESWITCH - * @param code bytecode - * @param n the number of case labels - * @param offsetPos the position of the branch-target table. - * @param defaultOffset the offset to the default branch target. - */ - protected void visitTableSwitch(int pos, byte[] code, int n, - int offsetPos, int defaultOffset) throws BadBytecode {} - - /** - * @param pos the position of LOOKUPSWITCH - * @param code bytecode - * @param n the number of case labels - * @param pairsPos the position of the table of pairs of a value and a branch target. - * @param defaultOffset the offset to the default branch target. - */ - protected void visitLookupSwitch(int pos, byte[] code, int n, - int pairsPos, int defaultOffset) throws BadBytecode {} - - /** - * Invoked when the visited instruction is jsr. - * Java6 or later does not allow using RET. - */ - protected void visitJSR(int pos, byte[] code) throws BadBytecode { - /* Since JSR pushes a return address onto the operand stack, - * the stack map at the entry point of a subroutine is - * stackTypes resulting after executing the following code: - * - * stackTypes[stackTop++] = TOP; - */ - } - - /** - * Invoked when the visited instruction is ret or wide ret. - * Java6 or later does not allow using RET. - */ - protected void visitRET(int pos, byte[] code) throws BadBytecode {} - - private int doOpcode0_53(int pos, byte[] code, int op) throws BadBytecode { - int reg; - TypeData[] stackTypes = this.stackTypes; - switch (op) { - case Opcode.NOP : - break; - case Opcode.ACONST_NULL : - stackTypes[stackTop++] = new TypeData.NullType(); - break; - case Opcode.ICONST_M1 : - case Opcode.ICONST_0 : - case Opcode.ICONST_1 : - case Opcode.ICONST_2 : - case Opcode.ICONST_3 : - case Opcode.ICONST_4 : - case Opcode.ICONST_5 : - stackTypes[stackTop++] = INTEGER; - break; - case Opcode.LCONST_0 : - case Opcode.LCONST_1 : - stackTypes[stackTop++] = LONG; - stackTypes[stackTop++] = TOP; - break; - case Opcode.FCONST_0 : - case Opcode.FCONST_1 : - case Opcode.FCONST_2 : - stackTypes[stackTop++] = FLOAT; - break; - case Opcode.DCONST_0 : - case Opcode.DCONST_1 : - stackTypes[stackTop++] = DOUBLE; - stackTypes[stackTop++] = TOP; - break; - case Opcode.BIPUSH : - case Opcode.SIPUSH : - stackTypes[stackTop++] = INTEGER; - return op == Opcode.SIPUSH ? 3 : 2; - case Opcode.LDC : - doLDC(code[pos + 1] & 0xff); - return 2; - case Opcode.LDC_W : - case Opcode.LDC2_W : - doLDC(ByteArray.readU16bit(code, pos + 1)); - return 3; - case Opcode.ILOAD : - return doXLOAD(INTEGER, code, pos); - case Opcode.LLOAD : - return doXLOAD(LONG, code, pos); - case Opcode.FLOAD : - return doXLOAD(FLOAT, code, pos); - case Opcode.DLOAD : - return doXLOAD(DOUBLE, code, pos); - case Opcode.ALOAD : - return doALOAD(code[pos + 1] & 0xff); - case Opcode.ILOAD_0 : - case Opcode.ILOAD_1 : - case Opcode.ILOAD_2 : - case Opcode.ILOAD_3 : - stackTypes[stackTop++] = INTEGER; - break; - case Opcode.LLOAD_0 : - case Opcode.LLOAD_1 : - case Opcode.LLOAD_2 : - case Opcode.LLOAD_3 : - stackTypes[stackTop++] = LONG; - stackTypes[stackTop++] = TOP; - break; - case Opcode.FLOAD_0 : - case Opcode.FLOAD_1 : - case Opcode.FLOAD_2 : - case Opcode.FLOAD_3 : - stackTypes[stackTop++] = FLOAT; - break; - case Opcode.DLOAD_0 : - case Opcode.DLOAD_1 : - case Opcode.DLOAD_2 : - case Opcode.DLOAD_3 : - stackTypes[stackTop++] = DOUBLE; - stackTypes[stackTop++] = TOP; - break; - case Opcode.ALOAD_0 : - case Opcode.ALOAD_1 : - case Opcode.ALOAD_2 : - case Opcode.ALOAD_3 : - reg = op - Opcode.ALOAD_0; - stackTypes[stackTop++] = localsTypes[reg]; - break; - case Opcode.IALOAD : - stackTypes[--stackTop - 1] = INTEGER; - break; - case Opcode.LALOAD : - stackTypes[stackTop - 2] = LONG; - stackTypes[stackTop - 1] = TOP; - break; - case Opcode.FALOAD : - stackTypes[--stackTop - 1] = FLOAT; - break; - case Opcode.DALOAD : - stackTypes[stackTop - 2] = DOUBLE; - stackTypes[stackTop - 1] = TOP; - break; - case Opcode.AALOAD : { - int s = --stackTop - 1; - TypeData data = stackTypes[s]; - stackTypes[s] = TypeData.ArrayElement.make(data); - break; } - case Opcode.BALOAD : - case Opcode.CALOAD : - case Opcode.SALOAD : - stackTypes[--stackTop - 1] = INTEGER; - break; - default : - throw new RuntimeException("fatal"); - } - - return 1; - } - - private void doLDC(int index) { - TypeData[] stackTypes = this.stackTypes; - int tag = cpool.getTag(index); - if (tag == ConstPool.CONST_String) - stackTypes[stackTop++] = new TypeData.ClassName("java.lang.String"); - else if (tag == ConstPool.CONST_Integer) - stackTypes[stackTop++] = INTEGER; - else if (tag == ConstPool.CONST_Float) - stackTypes[stackTop++] = FLOAT; - else if (tag == ConstPool.CONST_Long) { - stackTypes[stackTop++] = LONG; - stackTypes[stackTop++] = TOP; - } - else if (tag == ConstPool.CONST_Double) { - stackTypes[stackTop++] = DOUBLE; - stackTypes[stackTop++] = TOP; - } - else if (tag == ConstPool.CONST_Class) - stackTypes[stackTop++] = new TypeData.ClassName("java.lang.Class"); - else - throw new RuntimeException("bad LDC: " + tag); - } - - private int doXLOAD(TypeData type, byte[] code, int pos) { - int localVar = code[pos + 1] & 0xff; - return doXLOAD(localVar, type); - } - - private int doXLOAD(int localVar, TypeData type) { - stackTypes[stackTop++] = type; - if (type.is2WordType()) - stackTypes[stackTop++] = TOP; - - return 2; - } - - private int doALOAD(int localVar) { - stackTypes[stackTop++] = localsTypes[localVar]; - return 2; - } - - private int doOpcode54_95(int pos, byte[] code, int op) throws BadBytecode { - switch (op) { - case Opcode.ISTORE : - return doXSTORE(pos, code, INTEGER); - case Opcode.LSTORE : - return doXSTORE(pos, code, LONG); - case Opcode.FSTORE : - return doXSTORE(pos, code, FLOAT); - case Opcode.DSTORE : - return doXSTORE(pos, code, DOUBLE); - case Opcode.ASTORE : - return doASTORE(code[pos + 1] & 0xff); - case Opcode.ISTORE_0 : - case Opcode.ISTORE_1 : - case Opcode.ISTORE_2 : - case Opcode.ISTORE_3 : - { int var = op - Opcode.ISTORE_0; - localsTypes[var] = INTEGER; - stackTop--; } - break; - case Opcode.LSTORE_0 : - case Opcode.LSTORE_1 : - case Opcode.LSTORE_2 : - case Opcode.LSTORE_3 : - { int var = op - Opcode.LSTORE_0; - localsTypes[var] = LONG; - localsTypes[var + 1] = TOP; - stackTop -= 2; } - break; - case Opcode.FSTORE_0 : - case Opcode.FSTORE_1 : - case Opcode.FSTORE_2 : - case Opcode.FSTORE_3 : - { int var = op - Opcode.FSTORE_0; - localsTypes[var] = FLOAT; - stackTop--; } - break; - case Opcode.DSTORE_0 : - case Opcode.DSTORE_1 : - case Opcode.DSTORE_2 : - case Opcode.DSTORE_3 : - { int var = op - Opcode.DSTORE_0; - localsTypes[var] = DOUBLE; - localsTypes[var + 1] = TOP; - stackTop -= 2; } - break; - case Opcode.ASTORE_0 : - case Opcode.ASTORE_1 : - case Opcode.ASTORE_2 : - case Opcode.ASTORE_3 : - { int var = op - Opcode.ASTORE_0; - doASTORE(var); - break; } - case Opcode.IASTORE : - case Opcode.LASTORE : - case Opcode.FASTORE : - case Opcode.DASTORE : - stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3; - break; - case Opcode.AASTORE : - TypeData.ArrayElement.aastore(stackTypes[stackTop - 3], - stackTypes[stackTop - 1], - classPool); - stackTop -= 3; - break; - case Opcode.BASTORE : - case Opcode.CASTORE : - case Opcode.SASTORE : - stackTop -= 3; - break; - case Opcode.POP : - stackTop--; - break; - case Opcode.POP2 : - stackTop -= 2; - break; - case Opcode.DUP : { - int sp = stackTop; - stackTypes[sp] = stackTypes[sp - 1]; - stackTop = sp + 1; - break; } - case Opcode.DUP_X1 : - case Opcode.DUP_X2 : { - int len = op - Opcode.DUP_X1 + 2; - doDUP_XX(1, len); - int sp = stackTop; - stackTypes[sp - len] = stackTypes[sp]; - stackTop = sp + 1; - break; } - case Opcode.DUP2 : - doDUP_XX(2, 2); - stackTop += 2; - break; - case Opcode.DUP2_X1 : - case Opcode.DUP2_X2 : { - int len = op - Opcode.DUP2_X1 + 3; - doDUP_XX(2, len); - int sp = stackTop; - stackTypes[sp - len] = stackTypes[sp]; - stackTypes[sp - len + 1] = stackTypes[sp + 1]; - stackTop = sp + 2; - break; } - case Opcode.SWAP : { - int sp = stackTop - 1; - TypeData t = stackTypes[sp]; - stackTypes[sp] = stackTypes[sp - 1]; - stackTypes[sp - 1] = t; - break; } - default : - throw new RuntimeException("fatal"); - } - - return 1; - } - - private int doXSTORE(int pos, byte[] code, TypeData type) { - int index = code[pos + 1] & 0xff; - return doXSTORE(index, type); - } - - private int doXSTORE(int index, TypeData type) { - stackTop--; - localsTypes[index] = type; - if (type.is2WordType()) { - stackTop--; - localsTypes[index + 1] = TOP; - } - - return 2; - } - - private int doASTORE(int index) { - stackTop--; - // implicit upcast might be done. - localsTypes[index] = stackTypes[stackTop]; - return 2; - } - - private void doDUP_XX(int delta, int len) { - TypeData types[] = stackTypes; - int sp = stackTop - 1; - int end = sp - len; - while (sp > end) { - types[sp + delta] = types[sp]; - sp--; - } - } - - private int doOpcode96_147(int pos, byte[] code, int op) { - if (op <= Opcode.LXOR) { // IADD...LXOR - stackTop += Opcode.STACK_GROW[op]; - return 1; - } - - switch (op) { - case Opcode.IINC : - // this does not call writeLocal(). - return 3; - case Opcode.I2L : - stackTypes[stackTop - 1] = LONG; - stackTypes[stackTop] = TOP; - stackTop++; - break; - case Opcode.I2F : - stackTypes[stackTop - 1] = FLOAT; - break; - case Opcode.I2D : - stackTypes[stackTop - 1] = DOUBLE; - stackTypes[stackTop] = TOP; - stackTop++; - break; - case Opcode.L2I : - stackTypes[--stackTop - 1] = INTEGER; - break; - case Opcode.L2F : - stackTypes[--stackTop - 1] = FLOAT; - break; - case Opcode.L2D : - stackTypes[stackTop - 2] = DOUBLE; - break; - case Opcode.F2I : - stackTypes[stackTop - 1] = INTEGER; - break; - case Opcode.F2L : - stackTypes[stackTop - 1] = LONG; - stackTypes[stackTop] = TOP; - stackTop++; - break; - case Opcode.F2D : - stackTypes[stackTop - 1] = DOUBLE; - stackTypes[stackTop] = TOP; - stackTop++; - break; - case Opcode.D2I : - stackTypes[--stackTop - 1] = INTEGER; - break; - case Opcode.D2L : - stackTypes[stackTop - 2] = LONG; - break; - case Opcode.D2F : - stackTypes[--stackTop - 1] = FLOAT; - break; - case Opcode.I2B : - case Opcode.I2C : - case Opcode.I2S : - break; - default : - throw new RuntimeException("fatal"); - } - - return 1; - } - - private int doOpcode148_201(int pos, byte[] code, int op) throws BadBytecode { - switch (op) { - case Opcode.LCMP : - stackTypes[stackTop - 4] = INTEGER; - stackTop -= 3; - break; - case Opcode.FCMPL : - case Opcode.FCMPG : - stackTypes[--stackTop - 1] = INTEGER; - break; - case Opcode.DCMPL : - case Opcode.DCMPG : - stackTypes[stackTop - 4] = INTEGER; - stackTop -= 3; - break; - case Opcode.IFEQ : - case Opcode.IFNE : - case Opcode.IFLT : - case Opcode.IFGE : - case Opcode.IFGT : - case Opcode.IFLE : - stackTop--; // branch - visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); - return 3; - case Opcode.IF_ICMPEQ : - case Opcode.IF_ICMPNE : - case Opcode.IF_ICMPLT : - case Opcode.IF_ICMPGE : - case Opcode.IF_ICMPGT : - case Opcode.IF_ICMPLE : - case Opcode.IF_ACMPEQ : - case Opcode.IF_ACMPNE : - stackTop -= 2; // branch - visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); - return 3; - case Opcode.GOTO : - visitGoto(pos, code, ByteArray.readS16bit(code, pos + 1)); - return 3; // branch - case Opcode.JSR : - visitJSR(pos, code); - return 3; // branch - case Opcode.RET : - visitRET(pos, code); - return 2; - case Opcode.TABLESWITCH : { - stackTop--; // branch - int pos2 = (pos & ~3) + 8; - int low = ByteArray.read32bit(code, pos2); - int high = ByteArray.read32bit(code, pos2 + 4); - int n = high - low + 1; - visitTableSwitch(pos, code, n, pos2 + 8, ByteArray.read32bit(code, pos2 - 4)); - return n * 4 + 16 - (pos & 3); } - case Opcode.LOOKUPSWITCH : { - stackTop--; // branch - int pos2 = (pos & ~3) + 8; - int n = ByteArray.read32bit(code, pos2); - visitLookupSwitch(pos, code, n, pos2 + 4, ByteArray.read32bit(code, pos2 - 4)); - return n * 8 + 12 - (pos & 3); } - case Opcode.IRETURN : - stackTop--; - visitReturn(pos, code); - break; - case Opcode.LRETURN : - stackTop -= 2; - visitReturn(pos, code); - break; - case Opcode.FRETURN : - stackTop--; - visitReturn(pos, code); - break; - case Opcode.DRETURN : - stackTop -= 2; - visitReturn(pos, code); - break; - case Opcode.ARETURN : - stackTypes[--stackTop].setType(returnType, classPool); - visitReturn(pos, code); - break; - case Opcode.RETURN : - visitReturn(pos, code); - break; - case Opcode.GETSTATIC : - return doGetField(pos, code, false); - case Opcode.PUTSTATIC : - return doPutField(pos, code, false); - case Opcode.GETFIELD : - return doGetField(pos, code, true); - case Opcode.PUTFIELD : - return doPutField(pos, code, true); - case Opcode.INVOKEVIRTUAL : - case Opcode.INVOKESPECIAL : - return doInvokeMethod(pos, code, true); - case Opcode.INVOKESTATIC : - return doInvokeMethod(pos, code, false); - case Opcode.INVOKEINTERFACE : - return doInvokeIntfMethod(pos, code); - case Opcode.INVOKEDYNAMIC : - return doInvokeDynamic(pos, code); - case Opcode.NEW : { - int i = ByteArray.readU16bit(code, pos + 1); - stackTypes[stackTop++] - = new TypeData.UninitData(pos, cpool.getClassInfo(i)); - return 3; } - case Opcode.NEWARRAY : - return doNEWARRAY(pos, code); - case Opcode.ANEWARRAY : { - int i = ByteArray.readU16bit(code, pos + 1); - String type = cpool.getClassInfo(i).replace('.', '/'); - if (type.charAt(0) == '[') - type = "[" + type; - else - type = "[L" + type + ";"; - - stackTypes[stackTop - 1] - = new TypeData.ClassName(type); - return 3; } - case Opcode.ARRAYLENGTH : - stackTypes[stackTop - 1].setType("[Ljava.lang.Object;", classPool); - stackTypes[stackTop - 1] = INTEGER; - break; - case Opcode.ATHROW : - stackTypes[--stackTop].setType("java.lang.Throwable", classPool); - visitThrow(pos, code); - break; - case Opcode.CHECKCAST : { - // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool); - int i = ByteArray.readU16bit(code, pos + 1); - String type = cpool.getClassInfo(i); - if (type.charAt(0) == '[') - type = type.replace('.', '/'); // getClassInfo() may return "[java.lang.Object;". - - stackTypes[stackTop - 1] = new TypeData.ClassName(type); - return 3; } - case Opcode.INSTANCEOF : - // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool); - stackTypes[stackTop - 1] = INTEGER; - return 3; - case Opcode.MONITORENTER : - case Opcode.MONITOREXIT : - stackTop--; - // TypeData.setType(stackTypes[stackTop], "java.lang.Object", classPool); - break; - case Opcode.WIDE : - return doWIDE(pos, code); - case Opcode.MULTIANEWARRAY : - return doMultiANewArray(pos, code); - case Opcode.IFNULL : - case Opcode.IFNONNULL : - stackTop--; // branch - visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); - return 3; - case Opcode.GOTO_W : - visitGoto(pos, code, ByteArray.read32bit(code, pos + 1)); - return 5; // branch - case Opcode.JSR_W : - visitJSR(pos, code); - return 5; - } - return 1; - } - - private int doWIDE(int pos, byte[] code) throws BadBytecode { - int op = code[pos + 1] & 0xff; - switch (op) { - case Opcode.ILOAD : - doWIDE_XLOAD(pos, code, INTEGER); - break; - case Opcode.LLOAD : - doWIDE_XLOAD(pos, code, LONG); - break; - case Opcode.FLOAD : - doWIDE_XLOAD(pos, code, FLOAT); - break; - case Opcode.DLOAD : - doWIDE_XLOAD(pos, code, DOUBLE); - break; - case Opcode.ALOAD : { - int index = ByteArray.readU16bit(code, pos + 2); - doALOAD(index); - break; } - case Opcode.ISTORE : - doWIDE_STORE(pos, code, INTEGER); - break; - case Opcode.LSTORE : - doWIDE_STORE(pos, code, LONG); - break; - case Opcode.FSTORE : - doWIDE_STORE(pos, code, FLOAT); - break; - case Opcode.DSTORE : - doWIDE_STORE(pos, code, DOUBLE); - break; - case Opcode.ASTORE : { - int index = ByteArray.readU16bit(code, pos + 2); - doASTORE(index); - break; } - case Opcode.IINC : - // this does not call writeLocal(). - return 6; - case Opcode.RET : - visitRET(pos, code); - break; - default : - throw new RuntimeException("bad WIDE instruction: " + op); - } - - return 4; - } - - private void doWIDE_XLOAD(int pos, byte[] code, TypeData type) { - int index = ByteArray.readU16bit(code, pos + 2); - doXLOAD(index, type); - } - - private void doWIDE_STORE(int pos, byte[] code, TypeData type) { - int index = ByteArray.readU16bit(code, pos + 2); - doXSTORE(index, type); - } - - private int doPutField(int pos, byte[] code, boolean notStatic) throws BadBytecode { - int index = ByteArray.readU16bit(code, pos + 1); - String desc = cpool.getFieldrefType(index); - stackTop -= Descriptor.dataSize(desc); - char c = desc.charAt(0); - if (c == 'L') - stackTypes[stackTop].setType(getFieldClassName(desc, 0), classPool); - else if (c == '[') - stackTypes[stackTop].setType(desc, classPool); - - setFieldTarget(notStatic, index); - return 3; - } - - private int doGetField(int pos, byte[] code, boolean notStatic) throws BadBytecode { - int index = ByteArray.readU16bit(code, pos + 1); - setFieldTarget(notStatic, index); - String desc = cpool.getFieldrefType(index); - pushMemberType(desc); - return 3; - } - - private void setFieldTarget(boolean notStatic, int index) throws BadBytecode { - if (notStatic) { - String className = cpool.getFieldrefClassName(index); - stackTypes[--stackTop].setType(className, classPool); - } - } - - private int doNEWARRAY(int pos, byte[] code) { - int s = stackTop - 1; - String type; - switch (code[pos + 1] & 0xff) { - case Opcode.T_BOOLEAN : - type = "[Z"; - break; - case Opcode.T_CHAR : - type = "[C"; - break; - case Opcode.T_FLOAT : - type = "[F"; - break; - case Opcode.T_DOUBLE : - type = "[D"; - break; - case Opcode.T_BYTE : - type = "[B"; - break; - case Opcode.T_SHORT : - type = "[S"; - break; - case Opcode.T_INT : - type = "[I"; - break; - case Opcode.T_LONG : - type = "[J"; - break; - default : - throw new RuntimeException("bad newarray"); - } - - stackTypes[s] = new TypeData.ClassName(type); - return 2; - } - - private int doMultiANewArray(int pos, byte[] code) { - int i = ByteArray.readU16bit(code, pos + 1); - int dim = code[pos + 3] & 0xff; - stackTop -= dim - 1; - - String type = cpool.getClassInfo(i).replace('.', '/'); - stackTypes[stackTop - 1] = new TypeData.ClassName(type); - return 4; - } - - private int doInvokeMethod(int pos, byte[] code, boolean notStatic) throws BadBytecode { - int i = ByteArray.readU16bit(code, pos + 1); - String desc = cpool.getMethodrefType(i); - checkParamTypes(desc, 1); - if (notStatic) { - String className = cpool.getMethodrefClassName(i); - TypeData target = stackTypes[--stackTop]; - if (target instanceof TypeData.UninitTypeVar && target.isUninit()) - constructorCalled(target, ((TypeData.UninitTypeVar)target).offset()); - else if (target instanceof TypeData.UninitData) - constructorCalled(target, ((TypeData.UninitData)target).offset()); - - target.setType(className, classPool); - } - - pushMemberType(desc); - return 3; - } - - /* This is a constructor call on an uninitialized object. - * Sets flags of other references to that object. - * - * @param offset the offset where the object has been created. - */ - private void constructorCalled(TypeData target, int offset) { - target.constructorCalled(offset); - for (int i = 0; i < stackTop; i++) - stackTypes[i].constructorCalled(offset); - - for (int i = 0; i < localsTypes.length; i++) - localsTypes[i].constructorCalled(offset); - } - - private int doInvokeIntfMethod(int pos, byte[] code) throws BadBytecode { - int i = ByteArray.readU16bit(code, pos + 1); - String desc = cpool.getInterfaceMethodrefType(i); - checkParamTypes(desc, 1); - String className = cpool.getInterfaceMethodrefClassName(i); - stackTypes[--stackTop].setType(className, classPool); - pushMemberType(desc); - return 5; - } - - private int doInvokeDynamic(int pos, byte[] code) throws BadBytecode { - int i = ByteArray.readU16bit(code, pos + 1); - String desc = cpool.getInvokeDynamicType(i); - checkParamTypes(desc, 1); - - // assume CosntPool#REF_invokeStatic - /* TypeData target = stackTypes[--stackTop]; - if (target instanceof TypeData.UninitTypeVar && target.isUninit()) - constructorCalled((TypeData.UninitTypeVar)target); - */ - - pushMemberType(desc); - return 5; - } - - private void pushMemberType(String descriptor) { - int top = 0; - if (descriptor.charAt(0) == '(') { - top = descriptor.indexOf(')') + 1; - if (top < 1) - throw new IndexOutOfBoundsException("bad descriptor: " - + descriptor); - } - - TypeData[] types = stackTypes; - int index = stackTop; - switch (descriptor.charAt(top)) { - case '[' : - types[index] = new TypeData.ClassName(descriptor.substring(top)); - break; - case 'L' : - types[index] = new TypeData.ClassName(getFieldClassName(descriptor, top)); - break; - case 'J' : - types[index] = LONG; - types[index + 1] = TOP; - stackTop += 2; - return; - case 'F' : - types[index] = FLOAT; - break; - case 'D' : - types[index] = DOUBLE; - types[index + 1] = TOP; - stackTop += 2; - return; - case 'V' : - return; - default : // C, B, S, I, Z - types[index] = INTEGER; - break; - } - - stackTop++; - } - - private static String getFieldClassName(String desc, int index) { - return desc.substring(index + 1, desc.length() - 1).replace('/', '.'); - } - - private void checkParamTypes(String desc, int i) throws BadBytecode { - char c = desc.charAt(i); - if (c == ')') - return; - - int k = i; - boolean array = false; - while (c == '[') { - array = true; - c = desc.charAt(++k); - } - - if (c == 'L') { - k = desc.indexOf(';', k) + 1; - if (k <= 0) - throw new IndexOutOfBoundsException("bad descriptor"); - } - else - k++; - - checkParamTypes(desc, k); - if (!array && (c == 'J' || c == 'D')) - stackTop -= 2; - else - stackTop--; - - if (array) - stackTypes[stackTop].setType(desc.substring(i, k), classPool); - else if (c == 'L') - stackTypes[stackTop].setType(desc.substring(i + 1, k - 1).replace('/', '.'), - classPool); - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java b/src/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java deleted file mode 100644 index afab68c..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.stackmap; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.bytecode.StackMapTable; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import java.util.HashSet; -import java.util.Iterator; -import java.util.ArrayList; - -public abstract class TypeData { - /* Memo: - * array type is a subtype of Cloneable and Serializable - */ - - public static TypeData[] make(int size) { - TypeData[] array = new TypeData[size]; - for (int i = 0; i < size; i++) - array[i] = TypeTag.TOP; - - return array; - } - - protected TypeData() {} - - /** - * Sets the type name of this object type. If the given type name is - * a subclass of the current type name, then the given name becomes - * the name of this object type. - * - * @param className dot-separated name unless the type is an array type. - */ - private static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode { - td.setType(className, cp); - } - - public abstract int getTypeTag(); - public abstract int getTypeData(ConstPool cp); - - public TypeData join() { return new TypeVar(this); } - - /** - * If the type is a basic type, this method normalizes the type - * and returns a BasicType object. Otherwise, it returns null. - */ - public abstract BasicType isBasicType(); - - public abstract boolean is2WordType(); - - /** - * Returns false if getName() returns a valid type name. - */ - public boolean isNullType() { return false; } - - public boolean isUninit() { return false; } - - public abstract boolean eq(TypeData d); - - public abstract String getName(); - public abstract void setType(String s, ClassPool cp) throws BadBytecode; - - // depth-first search - public int dfs(ArrayList order, int index, ClassPool cp) - throws NotFoundException - { - return index; - } - - /** - * Returns this if it is a TypeVar or a TypeVar that this - * type depends on. Otherwise, this method returns null. - * It is used by dfs(). - */ - protected TypeVar toTypeVar() { return null; } - - // see UninitTypeVar and UninitData - public void constructorCalled(int offset) {} - - /** - * Primitive types. - */ - protected static class BasicType extends TypeData { - private String name; - private int typeTag; - - public BasicType(String type, int tag) { - name = type; - typeTag = tag; - } - - public int getTypeTag() { return typeTag; } - public int getTypeData(ConstPool cp) { return 0; } - - public TypeData join() { - if (this == TypeTag.TOP) - return this; - else - return super.join(); - } - - public BasicType isBasicType() { return this; } - - public boolean is2WordType() { - return typeTag == StackMapTable.LONG - || typeTag == StackMapTable.DOUBLE; - } - - public boolean eq(TypeData d) { return this == d; } - - public String getName() { - return name; - } - - public void setType(String s, ClassPool cp) throws BadBytecode { - throw new BadBytecode("conflict: " + name + " and " + s); - } - - public String toString() { return name; } - } - - // a type variable - public static abstract class AbsTypeVar extends TypeData { - public AbsTypeVar() {} - public abstract void merge(TypeData t); - public int getTypeTag() { return StackMapTable.OBJECT; } - - public int getTypeData(ConstPool cp) { - return cp.addClassInfo(getName()); - } - - public boolean eq(TypeData d) { return getName().equals(d.getName()); } - } - - /* a type variable representing a class type or a basic type. - */ - public static class TypeVar extends AbsTypeVar { - protected ArrayList lowers; // lower bounds of this type. ArrayList - protected ArrayList usedBy; // reverse relations of lowers - protected ArrayList uppers; // upper bounds of this type. - protected String fixedType; - private boolean is2WordType; // cache - - public TypeVar(TypeData t) { - uppers = null; - lowers = new ArrayList(2); - usedBy = new ArrayList(2); - merge(t); - fixedType = null; - is2WordType = t.is2WordType(); - } - - public String getName() { - if (fixedType == null) - return ((TypeData)lowers.get(0)).getName(); - else - return fixedType; - } - - public BasicType isBasicType() { - if (fixedType == null) - return ((TypeData)lowers.get(0)).isBasicType(); - else - return null; - } - - public boolean is2WordType() { - if (fixedType == null) { - return is2WordType; - // return ((TypeData)lowers.get(0)).is2WordType(); - } - else - return false; - } - - public boolean isNullType() { - if (fixedType == null) - return ((TypeData)lowers.get(0)).isNullType(); - else - return false; - } - - public boolean isUninit() { - if (fixedType == null) - return ((TypeData)lowers.get(0)).isUninit(); - else - return false; - } - - public void merge(TypeData t) { - lowers.add(t); - if (t instanceof TypeVar) - ((TypeVar)t).usedBy.add(this); - } - - public int getTypeTag() { - /* If fixedType is null after calling dfs(), then this - type is NULL, Uninit, or a basic type. So call - getTypeTag() on the first element of lowers. */ - if (fixedType == null) - return ((TypeData)lowers.get(0)).getTypeTag(); - else - return super.getTypeTag(); - } - - public int getTypeData(ConstPool cp) { - if (fixedType == null) - return ((TypeData)lowers.get(0)).getTypeData(cp); - else - return super.getTypeData(cp); - } - - public void setType(String typeName, ClassPool cp) throws BadBytecode { - if (uppers == null) - uppers = new ArrayList(); - - uppers.add(typeName); - } - - protected TypeVar toTypeVar() { return this; } - - private int visited = 0; - private int smallest = 0; - private boolean inList = false; - - // depth-first serach - public int dfs(ArrayList preOrder, int index, ClassPool cp) throws NotFoundException { - if (visited > 0) - return index; // MapMaker.make() may call an already visited node. - - visited = smallest = ++index; - preOrder.add(this); - inList = true; - int n = lowers.size(); - for (int i = 0; i < n; i++) { - TypeVar child = ((TypeData)lowers.get(i)).toTypeVar(); - if (child != null) - if (child.visited == 0) { - index = child.dfs(preOrder, index, cp); - if (child.smallest < smallest) - smallest = child.smallest; - } - else if (child.inList) - if (child.visited < smallest) - smallest = child.visited; - } - - if (visited == smallest) { - ArrayList scc = new ArrayList(); // strongly connected component - TypeVar cv; - do { - cv = (TypeVar)preOrder.remove(preOrder.size() - 1); - cv.inList = false; - scc.add(cv); - } while (cv != this); - fixTypes(scc, cp); - } - - return index; - } - - private void fixTypes(ArrayList scc, ClassPool cp) throws NotFoundException { - HashSet lowersSet = new HashSet(); - boolean isBasicType = false; - TypeData kind = null; - int size = scc.size(); - for (int i = 0; i < size; i++) { - ArrayList tds = ((TypeVar)scc.get(i)).lowers; - int size2 = tds.size(); - for (int j = 0; j < size2; j++) { - TypeData d = (TypeData)tds.get(j); - BasicType bt = d.isBasicType(); - if (kind == null) { - if (bt == null) { - isBasicType = false; - kind = d; - /* If scc has only an UninitData, fixedType is kept null. - So lowerSet must be empty. If scc has not only an UninitData - but also another TypeData, an error must be thrown but this - error detection has not been implemented. */ - if (d.isUninit()) - break; - } - else { - isBasicType = true; - kind = bt; - } - } - else { - if ((bt == null && isBasicType) - || (bt != null && kind != bt)) { - isBasicType = true; - kind = TypeTag.TOP; - break; - } - } - - if (bt == null && !d.isNullType()) - lowersSet.add(d.getName()); - } - } - - if (isBasicType) { - for (int i = 0; i < size; i++) { - TypeVar cv = (TypeVar)scc.get(i); - cv.lowers.clear(); - cv.lowers.add(kind); - is2WordType = kind.is2WordType(); - } - } - else { - String typeName = fixTypes2(scc, lowersSet, cp); - for (int i = 0; i < size; i++) { - TypeVar cv = (TypeVar)scc.get(i); - cv.fixedType = typeName; - } - } - } - - private String fixTypes2(ArrayList scc, HashSet lowersSet, ClassPool cp) throws NotFoundException { - Iterator it = lowersSet.iterator(); - if (lowersSet.size() == 0) - return null; // only NullType - else if (lowersSet.size() == 1) - return (String)it.next(); - else { - CtClass cc = cp.get((String)it.next()); - while (it.hasNext()) - cc = commonSuperClassEx(cc, cp.get((String)it.next())); - - if (cc.getSuperclass() == null || isObjectArray(cc)) - cc = fixByUppers(scc, cp, new HashSet(), cc); - - if (cc.isArray()) - return Descriptor.toJvmName(cc); - else - return cc.getName(); - } - } - - private static boolean isObjectArray(CtClass cc) throws NotFoundException { - return cc.isArray() && cc.getComponentType().getSuperclass() == null; - } - - private CtClass fixByUppers(ArrayList users, ClassPool cp, HashSet visited, CtClass type) - throws NotFoundException - { - if (users == null) - return type; - - int size = users.size(); - for (int i = 0; i < size; i++) { - TypeVar t = (TypeVar)users.get(i); - if (!visited.add(t)) - return type; - - if (t.uppers != null) { - int s = t.uppers.size(); - for (int k = 0; k < s; k++) { - CtClass cc = cp.get((String)t.uppers.get(k)); - if (cc.subtypeOf(type)) - type = cc; - } - } - - type = fixByUppers(t.usedBy, cp, visited, type); - } - - return type; - } - } - - /** - * Finds the most specific common super class of the given classes - * by considering array types. - */ - public static CtClass commonSuperClassEx(CtClass one, CtClass two) throws NotFoundException { - if (one == two) - return one; - else if (one.isArray() && two.isArray()) { - CtClass ele1 = one.getComponentType(); - CtClass ele2 = two.getComponentType(); - CtClass element = commonSuperClassEx(ele1, ele2); - if (element == ele1) - return one; - else if (element == ele2) - return two; - else - return one.getClassPool().get(element == null ? "java.lang.Object" - : element.getName() + "[]"); - } - else if (one.isPrimitive() || two.isPrimitive()) - return null; // TOP - else if (one.isArray() || two.isArray()) // but !(one.isArray() && two.isArray()) - return one.getClassPool().get("java.lang.Object"); - else - return commonSuperClass(one, two); - } - - /** - * Finds the most specific common super class of the given classes. - * This method is a copy from javassist.bytecode.analysis.Type. - */ - public static CtClass commonSuperClass(CtClass one, CtClass two) throws NotFoundException { - CtClass deep = one; - CtClass shallow = two; - CtClass backupShallow = shallow; - CtClass backupDeep = deep; - - // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly - for (;;) { - // In case we get lucky, and find a match early - if (eq(deep, shallow) && deep.getSuperclass() != null) - return deep; - - CtClass deepSuper = deep.getSuperclass(); - CtClass shallowSuper = shallow.getSuperclass(); - - if (shallowSuper == null) { - // right, now reset shallow - shallow = backupShallow; - break; - } - - if (deepSuper == null) { - // wrong, swap them, since deep is now useless, its our tmp before we swap it - deep = backupDeep; - backupDeep = backupShallow; - backupShallow = deep; - - deep = shallow; - shallow = backupShallow; - break; - } - - deep = deepSuper; - shallow = shallowSuper; - } - - // Phase 2 - Move deepBackup up by (deep end - deep) - for (;;) { - deep = deep.getSuperclass(); - if (deep == null) - break; - - backupDeep = backupDeep.getSuperclass(); - } - - deep = backupDeep; - - // Phase 3 - The hierarchy positions are now aligned - // The common super class is easy to find now - while (!eq(deep, shallow)) { - deep = deep.getSuperclass(); - shallow = shallow.getSuperclass(); - } - - return deep; - } - - static boolean eq(CtClass one, CtClass two) { - return one == two || (one != null && two != null && one.getName().equals(two.getName())); - } - - public static void aastore(TypeData array, TypeData value, ClassPool cp) throws BadBytecode { - if (array instanceof AbsTypeVar) - if (!value.isNullType()) - ((AbsTypeVar)array).merge(ArrayType.make(value)); - - if (value instanceof AbsTypeVar) - if (array instanceof AbsTypeVar) - ArrayElement.make(array); // should call value.setType() later. - else if (array instanceof ClassName) { - if (!array.isNullType()) { - String type = ArrayElement.typeName(array.getName()); - value.setType(type, cp); - } - } - else - throw new BadBytecode("bad AASTORE: " + array); - } - - /* A type variable representing an array type. - * It is a decorator of another type variable. - */ - public static class ArrayType extends AbsTypeVar { - private AbsTypeVar element; - - private ArrayType(AbsTypeVar elementType) { - element = elementType; - } - - static TypeData make(TypeData element) throws BadBytecode { - if (element instanceof ArrayElement) - return ((ArrayElement)element).arrayType(); - else if (element instanceof AbsTypeVar) - return new ArrayType((AbsTypeVar)element); - else if (element instanceof ClassName) - if (!element.isNullType()) - return new ClassName(typeName(element.getName())); - - throw new BadBytecode("bad AASTORE: " + element); - } - - public void merge(TypeData t) { - try { - if (!t.isNullType()) - element.merge(ArrayElement.make(t)); - } - catch (BadBytecode e) { - // never happens - throw new RuntimeException("fatal: " + e); - } - } - - public String getName() { - return typeName(element.getName()); - } - - public AbsTypeVar elementType() { return element; } - - public BasicType isBasicType() { return null; } - public boolean is2WordType() { return false; } - - /* elementType must be a class name. Basic type names - * are not allowed. - */ - public static String typeName(String elementType) { - if (elementType.charAt(0) == '[') - return "[" + elementType; - else - return "[L" + elementType.replace('.', '/') + ";"; - } - - public void setType(String s, ClassPool cp) throws BadBytecode { - element.setType(ArrayElement.typeName(s), cp); - } - - protected TypeVar toTypeVar() { return element.toTypeVar(); } - - public int dfs(ArrayList order, int index, ClassPool cp) throws NotFoundException { - return element.dfs(order, index, cp); - } - } - - /* A type variable representing an array-element type. - * It is a decorator of another type variable. - */ - public static class ArrayElement extends AbsTypeVar { - private AbsTypeVar array; - - private ArrayElement(AbsTypeVar a) { // a is never null - array = a; - } - - public static TypeData make(TypeData array) throws BadBytecode { - if (array instanceof ArrayType) - return ((ArrayType)array).elementType(); - else if (array instanceof AbsTypeVar) - return new ArrayElement((AbsTypeVar)array); - else if (array instanceof ClassName) - if (!array.isNullType()) - return new ClassName(typeName(array.getName())); - - throw new BadBytecode("bad AASTORE: " + array); - } - - public void merge(TypeData t) { - try { - if (!t.isNullType()) - array.merge(ArrayType.make(t)); - } - catch (BadBytecode e) { - // never happens - throw new RuntimeException("fatal: " + e); - } - } - - public String getName() { - return typeName(array.getName()); - } - - public AbsTypeVar arrayType() { return array; } - - /* arrayType must be a class name. Basic type names are - * not allowed. - */ - - public BasicType isBasicType() { return null; } - - public boolean is2WordType() { return false; } - - private static String typeName(String arrayType) { - if (arrayType.length() > 1 && arrayType.charAt(0) == '[') { - char c = arrayType.charAt(1); - if (c == 'L') - return arrayType.substring(2, arrayType.length() - 1).replace('/', '.'); - else if (c == '[') - return arrayType.substring(1); - } - - return "java.lang.Object"; // the array type may be NullType - } - - public void setType(String s, ClassPool cp) throws BadBytecode { - array.setType(ArrayType.typeName(s), cp); - } - - protected TypeVar toTypeVar() { return array.toTypeVar(); } - - public int dfs(ArrayList order, int index, ClassPool cp) throws NotFoundException { - return array.dfs(order, index, cp); - } - } - - public static class UninitTypeVar extends AbsTypeVar { - protected TypeData type; // UninitData or TOP - - public UninitTypeVar(UninitData t) { type = t; } - public int getTypeTag() { return type.getTypeTag(); } - public int getTypeData(ConstPool cp) { return type.getTypeData(cp); } - public BasicType isBasicType() { return type.isBasicType(); } - public boolean is2WordType() { return type.is2WordType(); } - public boolean isUninit() { return type.isUninit(); } - public boolean eq(TypeData d) { return type.eq(d); } - public String getName() { return type.getName(); } - - protected TypeVar toTypeVar() { return null; } - public TypeData join() { return type.join(); } - - public void setType(String s, ClassPool cp) throws BadBytecode { - type.setType(s, cp); - } - - public void merge(TypeData t) { - if (!t.eq(type)) - type = TypeTag.TOP; - } - - public void constructorCalled(int offset) { - type.constructorCalled(offset); - } - - public int offset() { - if (type instanceof UninitData) - return ((UninitData)type).offset; - else // if type == TypeTag.TOP - throw new RuntimeException("not available"); - } - } - - /** - * Type data for OBJECT. - */ - public static class ClassName extends TypeData { - private String name; // dot separated. - - public ClassName(String n) { - name = n; - } - - public String getName() { - return name; - } - - public BasicType isBasicType() { return null; } - - public boolean is2WordType() { return false; } - - public int getTypeTag() { return StackMapTable.OBJECT; } - - public int getTypeData(ConstPool cp) { - return cp.addClassInfo(getName()); - } - - public boolean eq(TypeData d) { return name.equals(d.getName()); } - - public void setType(String typeName, ClassPool cp) throws BadBytecode {} - } - - /** - * Type data for NULL or OBJECT. - * The types represented by the instances of this class are - * initially NULL but will be OBJECT. - */ - public static class NullType extends ClassName { - public NullType() { - super("null-type"); // type name - } - - public int getTypeTag() { - return StackMapTable.NULL; - } - - public boolean isNullType() { return true; } - public int getTypeData(ConstPool cp) { return 0; } - } - - /** - * Type data for UNINIT. - */ - public static class UninitData extends ClassName { - int offset; - boolean initialized; - - UninitData(int offset, String className) { - super(className); - this.offset = offset; - this.initialized = false; - } - - public UninitData copy() { return new UninitData(offset, getName()); } - - public int getTypeTag() { - return StackMapTable.UNINIT; - } - - public int getTypeData(ConstPool cp) { - return offset; - } - - public TypeData join() { - if (initialized) - return new TypeVar(new ClassName(getName())); - else - return new UninitTypeVar(copy()); - } - - public boolean isUninit() { return true; } - - public boolean eq(TypeData d) { - if (d instanceof UninitData) { - UninitData ud = (UninitData)d; - return offset == ud.offset && getName().equals(ud.getName()); - } - else - return false; - } - - public String toString() { return "uninit:" + getName() + "@" + offset; } - - public int offset() { return offset; } - - public void constructorCalled(int offset) { - if (offset == this.offset) - initialized = true; - } - } - - public static class UninitThis extends UninitData { - UninitThis(String className) { - super(-1, className); - } - - public UninitData copy() { return new UninitThis(getName()); } - - public int getTypeTag() { - return StackMapTable.THIS; - } - - public int getTypeData(ConstPool cp) { - return 0; - } - - public String toString() { return "uninit:this"; } - } -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java b/src/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java deleted file mode 100644 index 89aa4e3..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.stackmap; - -import com.wenshuo.agent.javassist.bytecode.StackMapTable; - -public interface TypeTag { - String TOP_TYPE = "*top*"; - TypeData TOP = new TypeData.BasicType(TOP_TYPE, StackMapTable.TOP); - TypeData INTEGER = new TypeData.BasicType("int", StackMapTable.INTEGER); - TypeData FLOAT = new TypeData.BasicType("float", StackMapTable.FLOAT); - TypeData DOUBLE = new TypeData.BasicType("double", StackMapTable.DOUBLE); - TypeData LONG = new TypeData.BasicType("long", StackMapTable.LONG); - - // and NULL, THIS, OBJECT, UNINIT -} diff --git a/src/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java b/src/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java deleted file mode 100644 index 631bfbd..0000000 --- a/src/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.bytecode.stackmap; - -import com.wenshuo.agent.javassist.bytecode.*; - -public class TypedBlock extends BasicBlock { - public int stackTop, numLocals; - // localsTypes is set to non-null when this block is first visited by a MapMaker. - // see alreadySet(). - public TypeData[] localsTypes; - public TypeData[] stackTypes; - - /** - * Divides the method body into basic blocks. - * The type information of the first block is initialized. - * - * @param optimize if it is true and the method does not include - * branches, this method returns null. - */ - public static TypedBlock[] makeBlocks(MethodInfo minfo, CodeAttribute ca, - boolean optimize) - throws BadBytecode - { - TypedBlock[] blocks = (TypedBlock[])new Maker().make(minfo); - if (optimize && blocks.length < 2) - if (blocks.length == 0 || blocks[0].incoming == 0) - return null; - - ConstPool pool = minfo.getConstPool(); - boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0; - blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(), - pool.getClassName(), minfo.getDescriptor(), - isStatic, minfo.isConstructor()); - return blocks; - } - - protected TypedBlock(int pos) { - super(pos); - localsTypes = null; - } - - protected void toString2(StringBuffer sbuf) { - super.toString2(sbuf); - sbuf.append(",\n stack={"); - printTypes(sbuf, stackTop, stackTypes); - sbuf.append("}, locals={"); - printTypes(sbuf, numLocals, localsTypes); - sbuf.append('}'); - } - - private void printTypes(StringBuffer sbuf, int size, - TypeData[] types) { - if (types == null) - return; - - for (int i = 0; i < size; i++) { - if (i > 0) - sbuf.append(", "); - - TypeData td = types[i]; - sbuf.append(td == null ? "<>" : td.toString()); - } - } - - public boolean alreadySet() { - return localsTypes != null; - } - - public void setStackMap(int st, TypeData[] stack, int nl, TypeData[] locals) - throws BadBytecode - { - stackTop = st; - stackTypes = stack; - numLocals = nl; - localsTypes = locals; - } - - /* - * Computes the correct value of numLocals. - */ - public void resetNumLocals() { - if (localsTypes != null) { - int nl = localsTypes.length; - while (nl > 0 && localsTypes[nl - 1].isBasicType() == TypeTag.TOP) { - if (nl > 1) { - if (localsTypes[nl - 2].is2WordType()) - break; - } - - --nl; - } - - numLocals = nl; - } - } - - public static class Maker extends BasicBlock.Maker { - protected BasicBlock makeBlock(int pos) { - return new TypedBlock(pos); - } - - protected BasicBlock[] makeArray(int size) { - return new TypedBlock[size]; - } - } - - /** - * Initializes the first block by the given method descriptor. - * - * @param block the first basic block that this method initializes. - * @param className a dot-separated fully qualified class name. - * For example, javassist.bytecode.stackmap.BasicBlock. - * @param methodDesc method descriptor. - * @param isStatic true if the method is a static method. - * @param isConstructor true if the method is a constructor. - */ - void initFirstBlock(int maxStack, int maxLocals, String className, - String methodDesc, boolean isStatic, boolean isConstructor) - throws BadBytecode - { - if (methodDesc.charAt(0) != '(') - throw new BadBytecode("no method descriptor: " + methodDesc); - - stackTop = 0; - stackTypes = TypeData.make(maxStack); - TypeData[] locals = TypeData.make(maxLocals); - if (isConstructor) - locals[0] = new TypeData.UninitThis(className); - else if (!isStatic) - locals[0] = new TypeData.ClassName(className); - - int n = isStatic ? -1 : 0; - int i = 1; - try { - while ((i = descToTag(methodDesc, i, ++n, locals)) > 0) - if (locals[n].is2WordType()) - locals[++n] = TypeTag.TOP; - } - catch (StringIndexOutOfBoundsException e) { - throw new BadBytecode("bad method descriptor: " - + methodDesc); - } - - numLocals = n; - localsTypes = locals; - } - - private static int descToTag(String desc, int i, - int n, TypeData[] types) - throws BadBytecode - { - int i0 = i; - int arrayDim = 0; - char c = desc.charAt(i); - if (c == ')') - return 0; - - while (c == '[') { - ++arrayDim; - c = desc.charAt(++i); - } - - if (c == 'L') { - int i2 = desc.indexOf(';', ++i); - if (arrayDim > 0) - types[n] = new TypeData.ClassName(desc.substring(i0, ++i2)); - else - types[n] = new TypeData.ClassName(desc.substring(i0 + 1, ++i2 - 1) - .replace('/', '.')); - return i2; - } - else if (arrayDim > 0) { - types[n] = new TypeData.ClassName(desc.substring(i0, ++i)); - return i; - } - else { - TypeData t = toPrimitiveTag(c); - if (t == null) - throw new BadBytecode("bad method descriptor: " + desc); - - types[n] = t; - return i + 1; - } - } - - private static TypeData toPrimitiveTag(char c) { - switch (c) { - case 'Z' : - case 'C' : - case 'B' : - case 'S' : - case 'I' : - return TypeTag.INTEGER; - case 'J' : - return TypeTag.LONG; - case 'F' : - return TypeTag.FLOAT; - case 'D' : - return TypeTag.DOUBLE; - case 'V' : - default : - return null; - } - } - - public static String getRetType(String desc) { - int i = desc.indexOf(')'); - if (i < 0) - return "java.lang.Object"; - - char c = desc.charAt(i + 1); - if (c == '[') - return desc.substring(i + 1); - else if (c == 'L') - return desc.substring(i + 2, desc.length() - 1).replace('/', '.'); - else - return "java.lang.Object"; - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/AccessorMaker.java b/src/com/wenshuo/agent/javassist/compiler/AccessorMaker.java deleted file mode 100644 index f35025f..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/AccessorMaker.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import java.util.HashMap; - -/** - * AccessorMaker maintains accessors to private members of an enclosing - * class. It is necessary for compiling a method in an inner class. - */ -public class AccessorMaker { - private CtClass clazz; - private int uniqueNumber; - private HashMap accessors; - - static final String lastParamType = "javassist.runtime.Inner"; - - public AccessorMaker(CtClass c) { - clazz = c; - uniqueNumber = 1; - accessors = new HashMap(); - } - - public String getConstructor(CtClass c, String desc, MethodInfo orig) - throws CompileError - { - String key = ":" + desc; - String consDesc = (String)accessors.get(key); - if (consDesc != null) - return consDesc; // already exists. - - consDesc = Descriptor.appendParameter(lastParamType, desc); - ClassFile cf = clazz.getClassFile(); // turn on the modified flag. - try { - ConstPool cp = cf.getConstPool(); - ClassPool pool = clazz.getClassPool(); - MethodInfo minfo - = new MethodInfo(cp, MethodInfo.nameInit, consDesc); - minfo.setAccessFlags(0); - minfo.addAttribute(new SyntheticAttribute(cp)); - ExceptionsAttribute ea = orig.getExceptionsAttribute(); - if (ea != null) - minfo.addAttribute(ea.copy(cp, null)); - - CtClass[] params = Descriptor.getParameterTypes(desc, pool); - Bytecode code = new Bytecode(cp); - code.addAload(0); - int regno = 1; - for (int i = 0; i < params.length; ++i) - regno += code.addLoad(regno, params[i]); - code.setMaxLocals(regno + 1); // the last parameter is added. - code.addInvokespecial(clazz, MethodInfo.nameInit, desc); - - code.addReturn(null); - minfo.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(minfo); - } - catch (CannotCompileException e) { - throw new CompileError(e); - } - catch (NotFoundException e) { - throw new CompileError(e); - } - - accessors.put(key, consDesc); - return consDesc; - } - - /** - * Returns the name of the method for accessing a private method. - * - * @param name the name of the private method. - * @param desc the descriptor of the private method. - * @param accDesc the descriptor of the accessor method. The first - * parameter type is clazz. - * If the private method is static, - * accDesc must be identical to desc. - * - * @param orig the method info of the private method. - * @return - */ - public String getMethodAccessor(String name, String desc, String accDesc, - MethodInfo orig) - throws CompileError - { - String key = name + ":" + desc; - String accName = (String)accessors.get(key); - if (accName != null) - return accName; // already exists. - - ClassFile cf = clazz.getClassFile(); // turn on the modified flag. - accName = findAccessorName(cf); - try { - ConstPool cp = cf.getConstPool(); - ClassPool pool = clazz.getClassPool(); - MethodInfo minfo - = new MethodInfo(cp, accName, accDesc); - minfo.setAccessFlags(AccessFlag.STATIC); - minfo.addAttribute(new SyntheticAttribute(cp)); - ExceptionsAttribute ea = orig.getExceptionsAttribute(); - if (ea != null) - minfo.addAttribute(ea.copy(cp, null)); - - CtClass[] params = Descriptor.getParameterTypes(accDesc, pool); - int regno = 0; - Bytecode code = new Bytecode(cp); - for (int i = 0; i < params.length; ++i) - regno += code.addLoad(regno, params[i]); - - code.setMaxLocals(regno); - if (desc == accDesc) - code.addInvokestatic(clazz, name, desc); - else - code.addInvokevirtual(clazz, name, desc); - - code.addReturn(Descriptor.getReturnType(desc, pool)); - minfo.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(minfo); - } - catch (CannotCompileException e) { - throw new CompileError(e); - } - catch (NotFoundException e) { - throw new CompileError(e); - } - - accessors.put(key, accName); - return accName; - } - - /** - * Returns the method_info representing the added getter. - */ - public MethodInfo getFieldGetter(FieldInfo finfo, boolean is_static) - throws CompileError - { - String fieldName = finfo.getName(); - String key = fieldName + ":getter"; - Object res = accessors.get(key); - if (res != null) - return (MethodInfo)res; // already exists. - - ClassFile cf = clazz.getClassFile(); // turn on the modified flag. - String accName = findAccessorName(cf); - try { - ConstPool cp = cf.getConstPool(); - ClassPool pool = clazz.getClassPool(); - String fieldType = finfo.getDescriptor(); - String accDesc; - if (is_static) - accDesc = "()" + fieldType; - else - accDesc = "(" + Descriptor.of(clazz) + ")" + fieldType; - - MethodInfo minfo = new MethodInfo(cp, accName, accDesc); - minfo.setAccessFlags(AccessFlag.STATIC); - minfo.addAttribute(new SyntheticAttribute(cp)); - Bytecode code = new Bytecode(cp); - if (is_static) { - code.addGetstatic(Bytecode.THIS, fieldName, fieldType); - } - else { - code.addAload(0); - code.addGetfield(Bytecode.THIS, fieldName, fieldType); - code.setMaxLocals(1); - } - - code.addReturn(Descriptor.toCtClass(fieldType, pool)); - minfo.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(minfo); - accessors.put(key, minfo); - return minfo; - } - catch (CannotCompileException e) { - throw new CompileError(e); - } - catch (NotFoundException e) { - throw new CompileError(e); - } - } - - /** - * Returns the method_info representing the added setter. - */ - public MethodInfo getFieldSetter(FieldInfo finfo, boolean is_static) - throws CompileError - { - String fieldName = finfo.getName(); - String key = fieldName + ":setter"; - Object res = accessors.get(key); - if (res != null) - return (MethodInfo)res; // already exists. - - ClassFile cf = clazz.getClassFile(); // turn on the modified flag. - String accName = findAccessorName(cf); - try { - ConstPool cp = cf.getConstPool(); - ClassPool pool = clazz.getClassPool(); - String fieldType = finfo.getDescriptor(); - String accDesc; - if (is_static) - accDesc = "(" + fieldType + ")V"; - else - accDesc = "(" + Descriptor.of(clazz) + fieldType + ")V"; - - MethodInfo minfo = new MethodInfo(cp, accName, accDesc); - minfo.setAccessFlags(AccessFlag.STATIC); - minfo.addAttribute(new SyntheticAttribute(cp)); - Bytecode code = new Bytecode(cp); - int reg; - if (is_static) { - reg = code.addLoad(0, Descriptor.toCtClass(fieldType, pool)); - code.addPutstatic(Bytecode.THIS, fieldName, fieldType); - } - else { - code.addAload(0); - reg = code.addLoad(1, Descriptor.toCtClass(fieldType, pool)) - + 1; - code.addPutfield(Bytecode.THIS, fieldName, fieldType); - } - - code.addReturn(null); - code.setMaxLocals(reg); - minfo.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(minfo); - accessors.put(key, minfo); - return minfo; - } - catch (CannotCompileException e) { - throw new CompileError(e); - } - catch (NotFoundException e) { - throw new CompileError(e); - } - } - - private String findAccessorName(ClassFile cf) { - String accName; - do { - accName = "access$" + uniqueNumber++; - } while (cf.getMethod(accName) != null); - return accName; - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/CodeGen.java b/src/com/wenshuo/agent/javassist/compiler/CodeGen.java deleted file mode 100644 index 3345abc..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/CodeGen.java +++ /dev/null @@ -1,1955 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import java.util.ArrayList; -import java.util.Arrays; -import com.wenshuo.agent.javassist.compiler.ast.*; -import com.wenshuo.agent.javassist.bytecode.*; - -/* The code generator is implemeted by three files: - * CodeGen.java, MemberCodeGen.java, and JvstCodeGen. - * I just wanted to split a big file into three smaller ones. - */ - -public abstract class CodeGen extends Visitor implements Opcode, TokenId { - static final String javaLangObject = "java.lang.Object"; - static final String jvmJavaLangObject = "java/lang/Object"; - - static final String javaLangString = "java.lang.String"; - static final String jvmJavaLangString = "java/lang/String"; - - protected Bytecode bytecode; - private int tempVar; - TypeChecker typeChecker; - - /** - * true if the last visited node is a return statement. - */ - protected boolean hasReturned; - - /** - * Must be true if compilation is for a static method. - */ - public boolean inStaticMethod; - - protected ArrayList breakList, continueList; - - /** - * doit() in ReturnHook is called from atReturn(). - */ - protected static abstract class ReturnHook { - ReturnHook next; - - /** - * Returns true if the generated code ends with return, - * throw, or goto. - */ - protected abstract boolean doit(Bytecode b, int opcode); - - protected ReturnHook(CodeGen gen) { - next = gen.returnHooks; - gen.returnHooks = this; - } - - protected void remove(CodeGen gen) { - gen.returnHooks = next; - } - } - - protected ReturnHook returnHooks; - - /* The following fields are used by atXXX() methods - * for returning the type of the compiled expression. - */ - protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ... - protected int arrayDim; - protected String className; // JVM-internal representation - - public CodeGen(Bytecode b) { - bytecode = b; - tempVar = -1; - typeChecker = null; - hasReturned = false; - inStaticMethod = false; - breakList = null; - continueList = null; - returnHooks = null; - } - - public void setTypeChecker(TypeChecker checker) { - typeChecker = checker; - } - - protected static void fatal() throws CompileError { - throw new CompileError("fatal"); - } - - public static boolean is2word(int type, int dim) { - return dim == 0 && (type == DOUBLE || type == LONG); - } - - public int getMaxLocals() { return bytecode.getMaxLocals(); } - - public void setMaxLocals(int n) { - bytecode.setMaxLocals(n); - } - - protected void incMaxLocals(int size) { - bytecode.incMaxLocals(size); - } - - /** - * Returns a local variable that single or double words can be - * stored in. - */ - protected int getTempVar() { - if (tempVar < 0) { - tempVar = getMaxLocals(); - incMaxLocals(2); - } - - return tempVar; - } - - protected int getLocalVar(Declarator d) { - int v = d.getLocalVar(); - if (v < 0) { - v = getMaxLocals(); // delayed variable allocation. - d.setLocalVar(v); - incMaxLocals(1); - } - - return v; - } - - /** - * Returns the JVM-internal representation of this class name. - */ - protected abstract String getThisName(); - - /** - * Returns the JVM-internal representation of this super class name. - */ - protected abstract String getSuperName() throws CompileError; - - /* Converts a class name into a JVM-internal representation. - * - * It may also expand a simple class name to java.lang.*. - * For example, this converts Object into java/lang/Object. - */ - protected abstract String resolveClassName(ASTList name) - throws CompileError; - - /* Expands a simple class name to java.lang.*. - * For example, this converts Object into java/lang/Object. - */ - protected abstract String resolveClassName(String jvmClassName) - throws CompileError; - - /** - * @param name the JVM-internal representation. - * name is not exapnded to java.lang.*. - */ - protected static String toJvmArrayName(String name, int dim) { - if (name == null) - return null; - - if (dim == 0) - return name; - else { - StringBuffer sbuf = new StringBuffer(); - int d = dim; - while (d-- > 0) - sbuf.append('['); - - sbuf.append('L'); - sbuf.append(name); - sbuf.append(';'); - - return sbuf.toString(); - } - } - - protected static String toJvmTypeName(int type, int dim) { - char c = 'I'; - switch(type) { - case BOOLEAN : - c = 'Z'; - break; - case BYTE : - c = 'B'; - break; - case CHAR : - c = 'C'; - break; - case SHORT : - c = 'S'; - break; - case INT : - c = 'I'; - break; - case LONG : - c = 'J'; - break; - case FLOAT : - c = 'F'; - break; - case DOUBLE : - c = 'D'; - break; - case VOID : - c = 'V'; - break; - } - - StringBuffer sbuf = new StringBuffer(); - while (dim-- > 0) - sbuf.append('['); - - sbuf.append(c); - return sbuf.toString(); - } - - public void compileExpr(ASTree expr) throws CompileError { - doTypeCheck(expr); - expr.accept(this); - } - - public boolean compileBooleanExpr(boolean branchIf, ASTree expr) - throws CompileError - { - doTypeCheck(expr); - return booleanExpr(branchIf, expr); - } - - public void doTypeCheck(ASTree expr) throws CompileError { - if (typeChecker != null) - expr.accept(typeChecker); - } - - public void atASTList(ASTList n) throws CompileError { fatal(); } - - public void atPair(Pair n) throws CompileError { fatal(); } - - public void atSymbol(Symbol n) throws CompileError { fatal(); } - - public void atFieldDecl(FieldDecl field) throws CompileError { - field.getInit().accept(this); - } - - public void atMethodDecl(MethodDecl method) throws CompileError { - ASTList mods = method.getModifiers(); - setMaxLocals(1); - while (mods != null) { - Keyword k = (Keyword)mods.head(); - mods = mods.tail(); - if (k.get() == STATIC) { - setMaxLocals(0); - inStaticMethod = true; - } - } - - ASTList params = method.getParams(); - while (params != null) { - atDeclarator((Declarator)params.head()); - params = params.tail(); - } - - Stmnt s = method.getBody(); - atMethodBody(s, method.isConstructor(), - method.getReturn().getType() == VOID); - } - - /** - * @param isCons true if super() must be called. - * false if the method is a class initializer. - */ - public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid) - throws CompileError - { - if (s == null) - return; - - if (isCons && needsSuperCall(s)) - insertDefaultSuperCall(); - - hasReturned = false; - s.accept(this); - if (!hasReturned) - if (isVoid) { - bytecode.addOpcode(Opcode.RETURN); - hasReturned = true; - } - else - throw new CompileError("no return statement"); - } - - private boolean needsSuperCall(Stmnt body) throws CompileError { - if (body.getOperator() == BLOCK) - body = (Stmnt)body.head(); - - if (body != null && body.getOperator() == EXPR) { - ASTree expr = body.head(); - if (expr != null && expr instanceof Expr - && ((Expr)expr).getOperator() == CALL) { - ASTree target = ((Expr)expr).head(); - if (target instanceof Keyword) { - int token = ((Keyword)target).get(); - return token != THIS && token != SUPER; - } - } - } - - return true; - } - - protected abstract void insertDefaultSuperCall() throws CompileError; - - public void atStmnt(Stmnt st) throws CompileError { - if (st == null) - return; // empty - - int op = st.getOperator(); - if (op == EXPR) { - ASTree expr = st.getLeft(); - doTypeCheck(expr); - if (expr instanceof AssignExpr) - atAssignExpr((AssignExpr)expr, false); - else if (isPlusPlusExpr(expr)) { - Expr e = (Expr)expr; - atPlusPlus(e.getOperator(), e.oprand1(), e, false); - } - else { - expr.accept(this); - if (is2word(exprType, arrayDim)) - bytecode.addOpcode(POP2); - else if (exprType != VOID) - bytecode.addOpcode(POP); - } - } - else if (op == DECL || op == BLOCK) { - ASTList list = st; - while (list != null) { - ASTree h = list.head(); - list = list.tail(); - if (h != null) - h.accept(this); - } - } - else if (op == IF) - atIfStmnt(st); - else if (op == WHILE || op == DO) - atWhileStmnt(st, op == WHILE); - else if (op == FOR) - atForStmnt(st); - else if (op == BREAK || op == CONTINUE) - atBreakStmnt(st, op == BREAK); - else if (op == TokenId.RETURN) - atReturnStmnt(st); - else if (op == THROW) - atThrowStmnt(st); - else if (op == TRY) - atTryStmnt(st); - else if (op == SWITCH) - atSwitchStmnt(st); - else if (op == SYNCHRONIZED) - atSyncStmnt(st); - else { - // LABEL, SWITCH label stament might be null?. - hasReturned = false; - throw new CompileError( - "sorry, not supported statement: TokenId " + op); - } - } - - private void atIfStmnt(Stmnt st) throws CompileError { - ASTree expr = st.head(); - Stmnt thenp = (Stmnt)st.tail().head(); - Stmnt elsep = (Stmnt)st.tail().tail().head(); - if (compileBooleanExpr(false, expr)) { - hasReturned = false; - if (elsep != null) - elsep.accept(this); - - return; - } - - int pc = bytecode.currentPc(); - int pc2 = 0; - bytecode.addIndex(0); // correct later - - hasReturned = false; - if (thenp != null) - thenp.accept(this); - - boolean thenHasReturned = hasReturned; - hasReturned = false; - - if (elsep != null && !thenHasReturned) { - bytecode.addOpcode(Opcode.GOTO); - pc2 = bytecode.currentPc(); - bytecode.addIndex(0); - } - - bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); - if (elsep != null) { - elsep.accept(this); - if (!thenHasReturned) - bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); - - hasReturned = thenHasReturned && hasReturned; - } - } - - private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError { - ArrayList prevBreakList = breakList; - ArrayList prevContList = continueList; - breakList = new ArrayList(); - continueList = new ArrayList(); - - ASTree expr = st.head(); - Stmnt body = (Stmnt)st.tail(); - - int pc = 0; - if (notDo) { - bytecode.addOpcode(Opcode.GOTO); - pc = bytecode.currentPc(); - bytecode.addIndex(0); - } - - int pc2 = bytecode.currentPc(); - if (body != null) - body.accept(this); - - int pc3 = bytecode.currentPc(); - if (notDo) - bytecode.write16bit(pc, pc3 - pc + 1); - - boolean alwaysBranch = compileBooleanExpr(true, expr); - if (alwaysBranch) { - bytecode.addOpcode(Opcode.GOTO); - alwaysBranch = breakList.size() == 0; - } - - bytecode.addIndex(pc2 - bytecode.currentPc() + 1); - patchGoto(breakList, bytecode.currentPc()); - patchGoto(continueList, pc3); - continueList = prevContList; - breakList = prevBreakList; - hasReturned = alwaysBranch; - } - - protected void patchGoto(ArrayList list, int targetPc) { - int n = list.size(); - for (int i = 0; i < n; ++i) { - int pc = ((Integer)list.get(i)).intValue(); - bytecode.write16bit(pc, targetPc - pc + 1); - } - } - - private void atForStmnt(Stmnt st) throws CompileError { - ArrayList prevBreakList = breakList; - ArrayList prevContList = continueList; - breakList = new ArrayList(); - continueList = new ArrayList(); - - Stmnt init = (Stmnt)st.head(); - ASTList p = st.tail(); - ASTree expr = p.head(); - p = p.tail(); - Stmnt update = (Stmnt)p.head(); - Stmnt body = (Stmnt)p.tail(); - - if (init != null) - init.accept(this); - - int pc = bytecode.currentPc(); - int pc2 = 0; - if (expr != null) { - if (compileBooleanExpr(false, expr)) { - // in case of "for (...; false; ...)" - continueList = prevContList; - breakList = prevBreakList; - hasReturned = false; - return; - } - - pc2 = bytecode.currentPc(); - bytecode.addIndex(0); - } - - if (body != null) - body.accept(this); - - int pc3 = bytecode.currentPc(); - if (update != null) - update.accept(this); - - bytecode.addOpcode(Opcode.GOTO); - bytecode.addIndex(pc - bytecode.currentPc() + 1); - - int pc4 = bytecode.currentPc(); - if (expr != null) - bytecode.write16bit(pc2, pc4 - pc2 + 1); - - patchGoto(breakList, pc4); - patchGoto(continueList, pc3); - continueList = prevContList; - breakList = prevBreakList; - hasReturned = false; - } - - private void atSwitchStmnt(Stmnt st) throws CompileError { - compileExpr(st.head()); - - ArrayList prevBreakList = breakList; - breakList = new ArrayList(); - int opcodePc = bytecode.currentPc(); - bytecode.addOpcode(LOOKUPSWITCH); - int npads = 3 - (opcodePc & 3); - while (npads-- > 0) - bytecode.add(0); - - Stmnt body = (Stmnt)st.tail(); - int npairs = 0; - for (ASTList list = body; list != null; list = list.tail()) - if (((Stmnt)list.head()).getOperator() == CASE) - ++npairs; - - // opcodePc2 is the position at which the default jump offset is. - int opcodePc2 = bytecode.currentPc(); - bytecode.addGap(4); - bytecode.add32bit(npairs); - bytecode.addGap(npairs * 8); - - long[] pairs = new long[npairs]; - int ipairs = 0; - int defaultPc = -1; - for (ASTList list = body; list != null; list = list.tail()) { - Stmnt label = (Stmnt)list.head(); - int op = label.getOperator(); - if (op == DEFAULT) - defaultPc = bytecode.currentPc(); - else if (op != CASE) - fatal(); - else { - pairs[ipairs++] - = ((long)computeLabel(label.head()) << 32) + - ((long)(bytecode.currentPc() - opcodePc) & 0xffffffff); - } - - hasReturned = false; - ((Stmnt)label.tail()).accept(this); - } - - Arrays.sort(pairs); - int pc = opcodePc2 + 8; - for (int i = 0; i < npairs; ++i) { - bytecode.write32bit(pc, (int)(pairs[i] >>> 32)); - bytecode.write32bit(pc + 4, (int)pairs[i]); - pc += 8; - } - - if (defaultPc < 0 || breakList.size() > 0) - hasReturned = false; - - int endPc = bytecode.currentPc(); - if (defaultPc < 0) - defaultPc = endPc; - - bytecode.write32bit(opcodePc2, defaultPc - opcodePc); - - patchGoto(breakList, endPc); - breakList = prevBreakList; - } - - private int computeLabel(ASTree expr) throws CompileError { - doTypeCheck(expr); - expr = TypeChecker.stripPlusExpr(expr); - if (expr instanceof IntConst) - return (int)((IntConst)expr).get(); - else - throw new CompileError("bad case label"); - } - - private void atBreakStmnt(Stmnt st, boolean notCont) - throws CompileError - { - if (st.head() != null) - throw new CompileError( - "sorry, not support labeled break or continue"); - - bytecode.addOpcode(Opcode.GOTO); - Integer pc = new Integer(bytecode.currentPc()); - bytecode.addIndex(0); - if (notCont) - breakList.add(pc); - else - continueList.add(pc); - } - - protected void atReturnStmnt(Stmnt st) throws CompileError { - atReturnStmnt2(st.getLeft()); - } - - protected final void atReturnStmnt2(ASTree result) throws CompileError { - int op; - if (result == null) - op = Opcode.RETURN; - else { - compileExpr(result); - if (arrayDim > 0) - op = ARETURN; - else { - int type = exprType; - if (type == DOUBLE) - op = DRETURN; - else if (type == FLOAT) - op = FRETURN; - else if (type == LONG) - op = LRETURN; - else if (isRefType(type)) - op = ARETURN; - else - op = IRETURN; - } - } - - for (ReturnHook har = returnHooks; har != null; har = har.next) - if (har.doit(bytecode, op)) { - hasReturned = true; - return; - } - - bytecode.addOpcode(op); - hasReturned = true; - } - - private void atThrowStmnt(Stmnt st) throws CompileError { - ASTree e = st.getLeft(); - compileExpr(e); - if (exprType != CLASS || arrayDim > 0) - throw new CompileError("bad throw statement"); - - bytecode.addOpcode(ATHROW); - hasReturned = true; - } - - /* overridden in MemberCodeGen - */ - protected void atTryStmnt(Stmnt st) throws CompileError { - hasReturned = false; - } - - private void atSyncStmnt(Stmnt st) throws CompileError { - int nbreaks = getListSize(breakList); - int ncontinues = getListSize(continueList); - - compileExpr(st.head()); - if (exprType != CLASS && arrayDim == 0) - throw new CompileError("bad type expr for synchronized block"); - - Bytecode bc = bytecode; - final int var = bc.getMaxLocals(); - bc.incMaxLocals(1); - bc.addOpcode(DUP); - bc.addAstore(var); - bc.addOpcode(MONITORENTER); - - ReturnHook rh = new ReturnHook(this) { - protected boolean doit(Bytecode b, int opcode) { - b.addAload(var); - b.addOpcode(MONITOREXIT); - return false; - } - }; - - int pc = bc.currentPc(); - Stmnt body = (Stmnt)st.tail(); - if (body != null) - body.accept(this); - - int pc2 = bc.currentPc(); - int pc3 = 0; - if (!hasReturned) { - rh.doit(bc, 0); // the 2nd arg is ignored. - bc.addOpcode(Opcode.GOTO); - pc3 = bc.currentPc(); - bc.addIndex(0); - } - - if (pc < pc2) { // if the body is not empty - int pc4 = bc.currentPc(); - rh.doit(bc, 0); // the 2nd arg is ignored. - bc.addOpcode(ATHROW); - bc.addExceptionHandler(pc, pc2, pc4, 0); - } - - if (!hasReturned) - bc.write16bit(pc3, bc.currentPc() - pc3 + 1); - - rh.remove(this); - - if (getListSize(breakList) != nbreaks - || getListSize(continueList) != ncontinues) - throw new CompileError( - "sorry, cannot break/continue in synchronized block"); - } - - private static int getListSize(ArrayList list) { - return list == null ? 0 : list.size(); - } - - private static boolean isPlusPlusExpr(ASTree expr) { - if (expr instanceof Expr) { - int op = ((Expr)expr).getOperator(); - return op == PLUSPLUS || op == MINUSMINUS; - } - - return false; - } - - public void atDeclarator(Declarator d) throws CompileError { - d.setLocalVar(getMaxLocals()); - d.setClassName(resolveClassName(d.getClassName())); - - int size; - if (is2word(d.getType(), d.getArrayDim())) - size = 2; - else - size = 1; - - incMaxLocals(size); - - /* NOTE: Array initializers has not been supported. - */ - ASTree init = d.getInitializer(); - if (init != null) { - doTypeCheck(init); - atVariableAssign(null, '=', null, d, init, false); - } - } - - public abstract void atNewExpr(NewExpr n) throws CompileError; - - public abstract void atArrayInit(ArrayInit init) throws CompileError; - - public void atAssignExpr(AssignExpr expr) throws CompileError { - atAssignExpr(expr, true); - } - - protected void atAssignExpr(AssignExpr expr, boolean doDup) - throws CompileError - { - // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>= - int op = expr.getOperator(); - ASTree left = expr.oprand1(); - ASTree right = expr.oprand2(); - if (left instanceof Variable) - atVariableAssign(expr, op, (Variable)left, - ((Variable)left).getDeclarator(), - right, doDup); - else { - if (left instanceof Expr) { - Expr e = (Expr)left; - if (e.getOperator() == ARRAY) { - atArrayAssign(expr, op, (Expr)left, right, doDup); - return; - } - } - - atFieldAssign(expr, op, left, right, doDup); - } - } - - protected static void badAssign(Expr expr) throws CompileError { - String msg; - if (expr == null) - msg = "incompatible type for assignment"; - else - msg = "incompatible type for " + expr.getName(); - - throw new CompileError(msg); - } - - /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. - * - * expr and var can be null. - */ - private void atVariableAssign(Expr expr, int op, Variable var, - Declarator d, ASTree right, - boolean doDup) throws CompileError - { - int varType = d.getType(); - int varArray = d.getArrayDim(); - String varClass = d.getClassName(); - int varNo = getLocalVar(d); - - if (op != '=') - atVariable(var); - - // expr is null if the caller is atDeclarator(). - if (expr == null && right instanceof ArrayInit) - atArrayVariableAssign((ArrayInit)right, varType, varArray, varClass); - else - atAssignCore(expr, op, right, varType, varArray, varClass); - - if (doDup) - if (is2word(varType, varArray)) - bytecode.addOpcode(DUP2); - else - bytecode.addOpcode(DUP); - - if (varArray > 0) - bytecode.addAstore(varNo); - else if (varType == DOUBLE) - bytecode.addDstore(varNo); - else if (varType == FLOAT) - bytecode.addFstore(varNo); - else if (varType == LONG) - bytecode.addLstore(varNo); - else if (isRefType(varType)) - bytecode.addAstore(varNo); - else - bytecode.addIstore(varNo); - - exprType = varType; - arrayDim = varArray; - className = varClass; - } - - protected abstract void atArrayVariableAssign(ArrayInit init, - int varType, int varArray, String varClass) throws CompileError; - - private void atArrayAssign(Expr expr, int op, Expr array, - ASTree right, boolean doDup) throws CompileError - { - arrayAccess(array.oprand1(), array.oprand2()); - - if (op != '=') { - bytecode.addOpcode(DUP2); - bytecode.addOpcode(getArrayReadOp(exprType, arrayDim)); - } - - int aType = exprType; - int aDim = arrayDim; - String cname = className; - - atAssignCore(expr, op, right, aType, aDim, cname); - - if (doDup) - if (is2word(aType, aDim)) - bytecode.addOpcode(DUP2_X2); - else - bytecode.addOpcode(DUP_X2); - - bytecode.addOpcode(getArrayWriteOp(aType, aDim)); - exprType = aType; - arrayDim = aDim; - className = cname; - } - - protected abstract void atFieldAssign(Expr expr, int op, ASTree left, - ASTree right, boolean doDup) throws CompileError; - - protected void atAssignCore(Expr expr, int op, ASTree right, - int type, int dim, String cname) - throws CompileError - { - if (op == PLUS_E && dim == 0 && type == CLASS) - atStringPlusEq(expr, type, dim, cname, right); - else { - right.accept(this); - if (invalidDim(exprType, arrayDim, className, type, dim, cname, - false) || (op != '=' && dim > 0)) - badAssign(expr); - - if (op != '=') { - int token = assignOps[op - MOD_E]; - int k = lookupBinOp(token); - if (k < 0) - fatal(); - - atArithBinExpr(expr, token, k, type); - } - } - - if (op != '=' || (dim == 0 && !isRefType(type))) - atNumCastExpr(exprType, type); - - // type check should be done here. - } - - private void atStringPlusEq(Expr expr, int type, int dim, String cname, - ASTree right) - throws CompileError - { - if (!jvmJavaLangString.equals(cname)) - badAssign(expr); - - convToString(type, dim); // the value might be null. - right.accept(this); - convToString(exprType, arrayDim); - bytecode.addInvokevirtual(javaLangString, "concat", - "(Ljava/lang/String;)Ljava/lang/String;"); - exprType = CLASS; - arrayDim = 0; - className = jvmJavaLangString; - } - - private boolean invalidDim(int srcType, int srcDim, String srcClass, - int destType, int destDim, String destClass, - boolean isCast) - { - if (srcDim != destDim) - if (srcType == NULL) - return false; - else if (destDim == 0 && destType == CLASS - && jvmJavaLangObject.equals(destClass)) - return false; - else if (isCast && srcDim == 0 && srcType == CLASS - && jvmJavaLangObject.equals(srcClass)) - return false; - else - return true; - - return false; - } - - public void atCondExpr(CondExpr expr) throws CompileError { - if (booleanExpr(false, expr.condExpr())) - expr.elseExpr().accept(this); - else { - int pc = bytecode.currentPc(); - bytecode.addIndex(0); // correct later - expr.thenExpr().accept(this); - int dim1 = arrayDim; - bytecode.addOpcode(Opcode.GOTO); - int pc2 = bytecode.currentPc(); - bytecode.addIndex(0); - bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); - expr.elseExpr().accept(this); - if (dim1 != arrayDim) - throw new CompileError("type mismatch in ?:"); - - bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); - } - } - - static final int[] binOp = { - '+', DADD, FADD, LADD, IADD, - '-', DSUB, FSUB, LSUB, ISUB, - '*', DMUL, FMUL, LMUL, IMUL, - '/', DDIV, FDIV, LDIV, IDIV, - '%', DREM, FREM, LREM, IREM, - '|', NOP, NOP, LOR, IOR, - '^', NOP, NOP, LXOR, IXOR, - '&', NOP, NOP, LAND, IAND, - LSHIFT, NOP, NOP, LSHL, ISHL, - RSHIFT, NOP, NOP, LSHR, ISHR, - ARSHIFT, NOP, NOP, LUSHR, IUSHR }; - - static int lookupBinOp(int token) { - int[] code = binOp; - int s = code.length; - for (int k = 0; k < s; k = k + 5) - if (code[k] == token) - return k; - - return -1; - } - - public void atBinExpr(BinExpr expr) throws CompileError { - int token = expr.getOperator(); - - /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>> - */ - int k = lookupBinOp(token); - if (k >= 0) { - expr.oprand1().accept(this); - ASTree right = expr.oprand2(); - if (right == null) - return; // see TypeChecker.atBinExpr(). - - int type1 = exprType; - int dim1 = arrayDim; - String cname1 = className; - right.accept(this); - if (dim1 != arrayDim) - throw new CompileError("incompatible array types"); - - if (token == '+' && dim1 == 0 - && (type1 == CLASS || exprType == CLASS)) - atStringConcatExpr(expr, type1, dim1, cname1); - else - atArithBinExpr(expr, token, k, type1); - } - else { - /* equation: &&, ||, ==, !=, <=, >=, <, > - */ - if (!booleanExpr(true, expr)) { - bytecode.addIndex(7); - bytecode.addIconst(0); // false - bytecode.addOpcode(Opcode.GOTO); - bytecode.addIndex(4); - } - - bytecode.addIconst(1); // true - } - } - - /* arrayDim values of the two oprands must be equal. - * If an oprand type is not a numeric type, this method - * throws an exception. - */ - private void atArithBinExpr(Expr expr, int token, - int index, int type1) throws CompileError - { - if (arrayDim != 0) - badTypes(expr); - - int type2 = exprType; - if (token == LSHIFT || token == RSHIFT || token == ARSHIFT) - if (type2 == INT || type2 == SHORT - || type2 == CHAR || type2 == BYTE) - exprType = type1; - else - badTypes(expr); - else - convertOprandTypes(type1, type2, expr); - - int p = typePrecedence(exprType); - if (p >= 0) { - int op = binOp[index + p + 1]; - if (op != NOP) { - if (p == P_INT && exprType != BOOLEAN) - exprType = INT; // type1 may be BYTE, ... - - bytecode.addOpcode(op); - return; - } - } - - badTypes(expr); - } - - private void atStringConcatExpr(Expr expr, int type1, int dim1, - String cname1) throws CompileError - { - int type2 = exprType; - int dim2 = arrayDim; - boolean type2Is2 = is2word(type2, dim2); - boolean type2IsString - = (type2 == CLASS && jvmJavaLangString.equals(className)); - - if (type2Is2) - convToString(type2, dim2); - - if (is2word(type1, dim1)) { - bytecode.addOpcode(DUP_X2); - bytecode.addOpcode(POP); - } - else - bytecode.addOpcode(SWAP); - - // even if type1 is String, the left operand might be null. - convToString(type1, dim1); - bytecode.addOpcode(SWAP); - - if (!type2Is2 && !type2IsString) - convToString(type2, dim2); - - bytecode.addInvokevirtual(javaLangString, "concat", - "(Ljava/lang/String;)Ljava/lang/String;"); - exprType = CLASS; - arrayDim = 0; - className = jvmJavaLangString; - } - - private void convToString(int type, int dim) throws CompileError { - final String method = "valueOf"; - - if (isRefType(type) || dim > 0) - bytecode.addInvokestatic(javaLangString, method, - "(Ljava/lang/Object;)Ljava/lang/String;"); - else if (type == DOUBLE) - bytecode.addInvokestatic(javaLangString, method, - "(D)Ljava/lang/String;"); - else if (type == FLOAT) - bytecode.addInvokestatic(javaLangString, method, - "(F)Ljava/lang/String;"); - else if (type == LONG) - bytecode.addInvokestatic(javaLangString, method, - "(J)Ljava/lang/String;"); - else if (type == BOOLEAN) - bytecode.addInvokestatic(javaLangString, method, - "(Z)Ljava/lang/String;"); - else if (type == CHAR) - bytecode.addInvokestatic(javaLangString, method, - "(C)Ljava/lang/String;"); - else if (type == VOID) - throw new CompileError("void type expression"); - else /* INT, BYTE, SHORT */ - bytecode.addInvokestatic(javaLangString, method, - "(I)Ljava/lang/String;"); - } - - /* Produces the opcode to branch if the condition is true. - * The oprand (branch offset) is not produced. - * - * @return true if the compiled code is GOTO (always branch). - * GOTO is not produced. - */ - private boolean booleanExpr(boolean branchIf, ASTree expr) - throws CompileError - { - boolean isAndAnd; - int op = getCompOperator(expr); - if (op == EQ) { // ==, !=, ... - BinExpr bexpr = (BinExpr)expr; - int type1 = compileOprands(bexpr); - // here, arrayDim might represent the array dim. of the left oprand - // if the right oprand is NULL. - compareExpr(branchIf, bexpr.getOperator(), type1, bexpr); - } - else if (op == '!') - return booleanExpr(!branchIf, ((Expr)expr).oprand1()); - else if ((isAndAnd = (op == ANDAND)) || op == OROR) { - BinExpr bexpr = (BinExpr)expr; - if (booleanExpr(!isAndAnd, bexpr.oprand1())) { - exprType = BOOLEAN; - arrayDim = 0; - return true; - } - else { - int pc = bytecode.currentPc(); - bytecode.addIndex(0); // correct later - if (booleanExpr(isAndAnd, bexpr.oprand2())) - bytecode.addOpcode(Opcode.GOTO); - - bytecode.write16bit(pc, bytecode.currentPc() - pc + 3); - if (branchIf != isAndAnd) { - bytecode.addIndex(6); // skip GOTO instruction - bytecode.addOpcode(Opcode.GOTO); - } - } - } - else if (isAlwaysBranch(expr, branchIf)) { - // Opcode.GOTO is not added here. The caller must add it. - exprType = BOOLEAN; - arrayDim = 0; - return true; // always branch - } - else { // others - expr.accept(this); - if (exprType != BOOLEAN || arrayDim != 0) - throw new CompileError("boolean expr is required"); - - bytecode.addOpcode(branchIf ? IFNE : IFEQ); - } - - exprType = BOOLEAN; - arrayDim = 0; - return false; - } - - private static boolean isAlwaysBranch(ASTree expr, boolean branchIf) { - if (expr instanceof Keyword) { - int t = ((Keyword)expr).get(); - return branchIf ? t == TRUE : t == FALSE; - } - - return false; - } - - static int getCompOperator(ASTree expr) throws CompileError { - if (expr instanceof Expr) { - Expr bexpr = (Expr)expr; - int token = bexpr.getOperator(); - if (token == '!') - return '!'; - else if ((bexpr instanceof BinExpr) - && token != OROR && token != ANDAND - && token != '&' && token != '|') - return EQ; // ==, !=, ... - else - return token; - } - - return ' '; // others - } - - private int compileOprands(BinExpr expr) throws CompileError { - expr.oprand1().accept(this); - int type1 = exprType; - int dim1 = arrayDim; - expr.oprand2().accept(this); - if (dim1 != arrayDim) - if (type1 != NULL && exprType != NULL) - throw new CompileError("incompatible array types"); - else if (exprType == NULL) - arrayDim = dim1; - - if (type1 == NULL) - return exprType; - else - return type1; - } - - private static final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE, - NEQ, IF_ICMPNE, IF_ICMPEQ, - LE, IF_ICMPLE, IF_ICMPGT, - GE, IF_ICMPGE, IF_ICMPLT, - '<', IF_ICMPLT, IF_ICMPGE, - '>', IF_ICMPGT, IF_ICMPLE }; - - private static final int ifOp2[] = { EQ, IFEQ, IFNE, - NEQ, IFNE, IFEQ, - LE, IFLE, IFGT, - GE, IFGE, IFLT, - '<', IFLT, IFGE, - '>', IFGT, IFLE }; - - /* Produces the opcode to branch if the condition is true. - * The oprands are not produced. - * - * Parameter expr - compare expression ==, !=, <=, >=, <, > - */ - private void compareExpr(boolean branchIf, - int token, int type1, BinExpr expr) - throws CompileError - { - if (arrayDim == 0) - convertOprandTypes(type1, exprType, expr); - - int p = typePrecedence(exprType); - if (p == P_OTHER || arrayDim > 0) - if (token == EQ) - bytecode.addOpcode(branchIf ? IF_ACMPEQ : IF_ACMPNE); - else if (token == NEQ) - bytecode.addOpcode(branchIf ? IF_ACMPNE : IF_ACMPEQ); - else - badTypes(expr); - else - if (p == P_INT) { - int op[] = ifOp; - for (int i = 0; i < op.length; i += 3) - if (op[i] == token) { - bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]); - return; - } - - badTypes(expr); - } - else { - if (p == P_DOUBLE) - if (token == '<' || token == LE) - bytecode.addOpcode(DCMPG); - else - bytecode.addOpcode(DCMPL); - else if (p == P_FLOAT) - if (token == '<' || token == LE) - bytecode.addOpcode(FCMPG); - else - bytecode.addOpcode(FCMPL); - else if (p == P_LONG) - bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: < - else - fatal(); - - int[] op = ifOp2; - for (int i = 0; i < op.length; i += 3) - if (op[i] == token) { - bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]); - return; - } - - badTypes(expr); - } - } - - protected static void badTypes(Expr expr) throws CompileError { - throw new CompileError("invalid types for " + expr.getName()); - } - - private static final int P_DOUBLE = 0; - private static final int P_FLOAT = 1; - private static final int P_LONG = 2; - private static final int P_INT = 3; - private static final int P_OTHER = -1; - - protected static boolean isRefType(int type) { - return type == CLASS || type == NULL; - } - - private static int typePrecedence(int type) { - if (type == DOUBLE) - return P_DOUBLE; - else if (type == FLOAT) - return P_FLOAT; - else if (type == LONG) - return P_LONG; - else if (isRefType(type)) - return P_OTHER; - else if (type == VOID) - return P_OTHER; // this is wrong, but ... - else - return P_INT; // BOOLEAN, BYTE, CHAR, SHORT, INT - } - - // used in TypeChecker. - static boolean isP_INT(int type) { - return typePrecedence(type) == P_INT; - } - - // used in TypeChecker. - static boolean rightIsStrong(int type1, int type2) { - int type1_p = typePrecedence(type1); - int type2_p = typePrecedence(type2); - return type1_p >= 0 && type2_p >= 0 && type1_p > type2_p; - } - - private static final int[] castOp = { - /* D F L I */ - /* double */ NOP, D2F, D2L, D2I, - /* float */ F2D, NOP, F2L, F2I, - /* long */ L2D, L2F, NOP, L2I, - /* other */ I2D, I2F, I2L, NOP }; - - /* do implicit type conversion. - * arrayDim values of the two oprands must be zero. - */ - private void convertOprandTypes(int type1, int type2, Expr expr) - throws CompileError - { - boolean rightStrong; - int type1_p = typePrecedence(type1); - int type2_p = typePrecedence(type2); - - if (type2_p < 0 && type1_p < 0) // not primitive types - return; - - if (type2_p < 0 || type1_p < 0) // either is not a primitive type - badTypes(expr); - - int op, result_type; - if (type1_p <= type2_p) { - rightStrong = false; - exprType = type1; - op = castOp[type2_p * 4 + type1_p]; - result_type = type1_p; - } - else { - rightStrong = true; - op = castOp[type1_p * 4 + type2_p]; - result_type = type2_p; - } - - if (rightStrong) { - if (result_type == P_DOUBLE || result_type == P_LONG) { - if (type1_p == P_DOUBLE || type1_p == P_LONG) - bytecode.addOpcode(DUP2_X2); - else - bytecode.addOpcode(DUP2_X1); - - bytecode.addOpcode(POP2); - bytecode.addOpcode(op); - bytecode.addOpcode(DUP2_X2); - bytecode.addOpcode(POP2); - } - else if (result_type == P_FLOAT) { - if (type1_p == P_LONG) { - bytecode.addOpcode(DUP_X2); - bytecode.addOpcode(POP); - } - else - bytecode.addOpcode(SWAP); - - bytecode.addOpcode(op); - bytecode.addOpcode(SWAP); - } - else - fatal(); - } - else if (op != NOP) - bytecode.addOpcode(op); - } - - public void atCastExpr(CastExpr expr) throws CompileError { - String cname = resolveClassName(expr.getClassName()); - String toClass = checkCastExpr(expr, cname); - int srcType = exprType; - exprType = expr.getType(); - arrayDim = expr.getArrayDim(); - className = cname; - if (toClass == null) - atNumCastExpr(srcType, exprType); // built-in type - else - bytecode.addCheckcast(toClass); - } - - public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { - String cname = resolveClassName(expr.getClassName()); - String toClass = checkCastExpr(expr, cname); - bytecode.addInstanceof(toClass); - exprType = BOOLEAN; - arrayDim = 0; - } - - private String checkCastExpr(CastExpr expr, String name) - throws CompileError - { - final String msg = "invalid cast"; - ASTree oprand = expr.getOprand(); - int dim = expr.getArrayDim(); - int type = expr.getType(); - oprand.accept(this); - int srcType = exprType; - if (invalidDim(srcType, arrayDim, className, type, dim, name, true) - || srcType == VOID || type == VOID) - throw new CompileError(msg); - - if (type == CLASS) { - if (!isRefType(srcType)) - throw new CompileError(msg); - - return toJvmArrayName(name, dim); - } - else - if (dim > 0) - return toJvmTypeName(type, dim); - else - return null; // built-in type - } - - void atNumCastExpr(int srcType, int destType) - throws CompileError - { - if (srcType == destType) - return; - - int op, op2; - int stype = typePrecedence(srcType); - int dtype = typePrecedence(destType); - if (0 <= stype && stype < 3) - op = castOp[stype * 4 + dtype]; - else - op = NOP; - - if (destType == DOUBLE) - op2 = I2D; - else if (destType == FLOAT) - op2 = I2F; - else if (destType == LONG) - op2 = I2L; - else if (destType == SHORT) - op2 = I2S; - else if (destType == CHAR) - op2 = I2C; - else if (destType == BYTE) - op2 = I2B; - else - op2 = NOP; - - if (op != NOP) - bytecode.addOpcode(op); - - if (op == NOP || op == L2I || op == F2I || op == D2I) - if (op2 != NOP) - bytecode.addOpcode(op2); - } - - public void atExpr(Expr expr) throws CompileError { - // array access, member access, - // (unary) +, (unary) -, ++, --, !, ~ - - int token = expr.getOperator(); - ASTree oprand = expr.oprand1(); - if (token == '.') { - String member = ((Symbol)expr.oprand2()).get(); - if (member.equals("class")) - atClassObject(expr); // .class - else - atFieldRead(expr); - } - else if (token == MEMBER) { // field read - /* MEMBER ('#') is an extension by Javassist. - * The compiler internally uses # for compiling .class - * expressions such as "int.class". - */ - atFieldRead(expr); - } - else if (token == ARRAY) - atArrayRead(oprand, expr.oprand2()); - else if (token == PLUSPLUS || token == MINUSMINUS) - atPlusPlus(token, oprand, expr, true); - else if (token == '!') { - if (!booleanExpr(false, expr)) { - bytecode.addIndex(7); - bytecode.addIconst(1); - bytecode.addOpcode(Opcode.GOTO); - bytecode.addIndex(4); - } - - bytecode.addIconst(0); - } - else if (token == CALL) // method call - fatal(); - else { - expr.oprand1().accept(this); - int type = typePrecedence(exprType); - if (arrayDim > 0) - badType(expr); - - if (token == '-') { - if (type == P_DOUBLE) - bytecode.addOpcode(DNEG); - else if (type == P_FLOAT) - bytecode.addOpcode(FNEG); - else if (type == P_LONG) - bytecode.addOpcode(LNEG); - else if (type == P_INT) { - bytecode.addOpcode(INEG); - exprType = INT; // type may be BYTE, ... - } - else - badType(expr); - } - else if (token == '~') { - if (type == P_INT) { - bytecode.addIconst(-1); - bytecode.addOpcode(IXOR); - exprType = INT; // type may be BYTE. ... - } - else if (type == P_LONG) { - bytecode.addLconst(-1); - bytecode.addOpcode(LXOR); - } - else - badType(expr); - - } - else if (token == '+') { - if (type == P_OTHER) - badType(expr); - - // do nothing. ignore. - } - else - fatal(); - } - } - - protected static void badType(Expr expr) throws CompileError { - throw new CompileError("invalid type for " + expr.getName()); - } - - public abstract void atCallExpr(CallExpr expr) throws CompileError; - - protected abstract void atFieldRead(ASTree expr) throws CompileError; - - public void atClassObject(Expr expr) throws CompileError { - ASTree op1 = expr.oprand1(); - if (!(op1 instanceof Symbol)) - throw new CompileError("fatal error: badly parsed .class expr"); - - String cname = ((Symbol)op1).get(); - if (cname.startsWith("[")) { - int i = cname.indexOf("[L"); - if (i >= 0) { - String name = cname.substring(i + 2, cname.length() - 1); - String name2 = resolveClassName(name); - if (!name.equals(name2)) { - /* For example, to obtain String[].class, - * "[Ljava.lang.String;" (not "[Ljava/lang/String"!) - * must be passed to Class.forName(). - */ - name2 = MemberResolver.jvmToJavaName(name2); - StringBuffer sbuf = new StringBuffer(); - while (i-- >= 0) - sbuf.append('['); - - sbuf.append('L').append(name2).append(';'); - cname = sbuf.toString(); - } - } - } - else { - cname = resolveClassName(MemberResolver.javaToJvmName(cname)); - cname = MemberResolver.jvmToJavaName(cname); - } - - atClassObject2(cname); - exprType = CLASS; - arrayDim = 0; - className = "java/lang/Class"; - } - - /* MemberCodeGen overrides this method. - */ - protected void atClassObject2(String cname) throws CompileError { - int start = bytecode.currentPc(); - bytecode.addLdc(cname); - bytecode.addInvokestatic("java.lang.Class", "forName", - "(Ljava/lang/String;)Ljava/lang/Class;"); - int end = bytecode.currentPc(); - bytecode.addOpcode(Opcode.GOTO); - int pc = bytecode.currentPc(); - bytecode.addIndex(0); // correct later - - bytecode.addExceptionHandler(start, end, bytecode.currentPc(), - "java.lang.ClassNotFoundException"); - - /* -- the following code is for inlining a call to DotClass.fail(). - - int var = getMaxLocals(); - incMaxLocals(1); - bytecode.growStack(1); - bytecode.addAstore(var); - - bytecode.addNew("java.lang.NoClassDefFoundError"); - bytecode.addOpcode(DUP); - bytecode.addAload(var); - bytecode.addInvokevirtual("java.lang.ClassNotFoundException", - "getMessage", "()Ljava/lang/String;"); - bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "", - "(Ljava/lang/String;)V"); - */ - - bytecode.growStack(1); - bytecode.addInvokestatic("javassist.runtime.DotClass", "fail", - "(Ljava/lang/ClassNotFoundException;)" - + "Ljava/lang/NoClassDefFoundError;"); - bytecode.addOpcode(ATHROW); - bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); - } - - public void atArrayRead(ASTree array, ASTree index) - throws CompileError - { - arrayAccess(array, index); - bytecode.addOpcode(getArrayReadOp(exprType, arrayDim)); - } - - protected void arrayAccess(ASTree array, ASTree index) - throws CompileError - { - array.accept(this); - int type = exprType; - int dim = arrayDim; - if (dim == 0) - throw new CompileError("bad array access"); - - String cname = className; - - index.accept(this); - if (typePrecedence(exprType) != P_INT || arrayDim > 0) - throw new CompileError("bad array index"); - - exprType = type; - arrayDim = dim - 1; - className = cname; - } - - protected static int getArrayReadOp(int type, int dim) { - if (dim > 0) - return AALOAD; - - switch (type) { - case DOUBLE : - return DALOAD; - case FLOAT : - return FALOAD; - case LONG : - return LALOAD; - case INT : - return IALOAD; - case SHORT : - return SALOAD; - case CHAR : - return CALOAD; - case BYTE : - case BOOLEAN : - return BALOAD; - default : - return AALOAD; - } - } - - protected static int getArrayWriteOp(int type, int dim) { - if (dim > 0) - return AASTORE; - - switch (type) { - case DOUBLE : - return DASTORE; - case FLOAT : - return FASTORE; - case LONG : - return LASTORE; - case INT : - return IASTORE; - case SHORT : - return SASTORE; - case CHAR : - return CASTORE; - case BYTE : - case BOOLEAN : - return BASTORE; - default : - return AASTORE; - } - } - - private void atPlusPlus(int token, ASTree oprand, Expr expr, - boolean doDup) throws CompileError - { - boolean isPost = oprand == null; // ++i or i++? - if (isPost) - oprand = expr.oprand2(); - - if (oprand instanceof Variable) { - Declarator d = ((Variable)oprand).getDeclarator(); - int t = exprType = d.getType(); - arrayDim = d.getArrayDim(); - int var = getLocalVar(d); - if (arrayDim > 0) - badType(expr); - - if (t == DOUBLE) { - bytecode.addDload(var); - if (doDup && isPost) - bytecode.addOpcode(DUP2); - - bytecode.addDconst(1.0); - bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB); - if (doDup && !isPost) - bytecode.addOpcode(DUP2); - - bytecode.addDstore(var); - } - else if (t == LONG) { - bytecode.addLload(var); - if (doDup && isPost) - bytecode.addOpcode(DUP2); - - bytecode.addLconst((long)1); - bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB); - if (doDup && !isPost) - bytecode.addOpcode(DUP2); - - bytecode.addLstore(var); - } - else if (t == FLOAT) { - bytecode.addFload(var); - if (doDup && isPost) - bytecode.addOpcode(DUP); - - bytecode.addFconst(1.0f); - bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB); - if (doDup && !isPost) - bytecode.addOpcode(DUP); - - bytecode.addFstore(var); - } - else if (t == BYTE || t == CHAR || t == SHORT || t == INT) { - if (doDup && isPost) - bytecode.addIload(var); - - int delta = token == PLUSPLUS ? 1 : -1; - if (var > 0xff) { - bytecode.addOpcode(WIDE); - bytecode.addOpcode(IINC); - bytecode.addIndex(var); - bytecode.addIndex(delta); - } - else { - bytecode.addOpcode(IINC); - bytecode.add(var); - bytecode.add(delta); - } - - if (doDup && !isPost) - bytecode.addIload(var); - } - else - badType(expr); - } - else { - if (oprand instanceof Expr) { - Expr e = (Expr)oprand; - if (e.getOperator() == ARRAY) { - atArrayPlusPlus(token, isPost, e, doDup); - return; - } - } - - atFieldPlusPlus(token, isPost, oprand, expr, doDup); - } - } - - public void atArrayPlusPlus(int token, boolean isPost, - Expr expr, boolean doDup) throws CompileError - { - arrayAccess(expr.oprand1(), expr.oprand2()); - int t = exprType; - int dim = arrayDim; - if (dim > 0) - badType(expr); - - bytecode.addOpcode(DUP2); - bytecode.addOpcode(getArrayReadOp(t, arrayDim)); - int dup_code = is2word(t, dim) ? DUP2_X2 : DUP_X2; - atPlusPlusCore(dup_code, doDup, token, isPost, expr); - bytecode.addOpcode(getArrayWriteOp(t, dim)); - } - - protected void atPlusPlusCore(int dup_code, boolean doDup, - int token, boolean isPost, - Expr expr) throws CompileError - { - int t = exprType; - - if (doDup && isPost) - bytecode.addOpcode(dup_code); - - if (t == INT || t == BYTE || t == CHAR || t == SHORT) { - bytecode.addIconst(1); - bytecode.addOpcode(token == PLUSPLUS ? IADD : ISUB); - exprType = INT; - } - else if (t == LONG) { - bytecode.addLconst((long)1); - bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB); - } - else if (t == FLOAT) { - bytecode.addFconst(1.0f); - bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB); - } - else if (t == DOUBLE) { - bytecode.addDconst(1.0); - bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB); - } - else - badType(expr); - - if (doDup && !isPost) - bytecode.addOpcode(dup_code); - } - - protected abstract void atFieldPlusPlus(int token, boolean isPost, - ASTree oprand, Expr expr, boolean doDup) throws CompileError; - - public abstract void atMember(Member n) throws CompileError; - - public void atVariable(Variable v) throws CompileError { - Declarator d = v.getDeclarator(); - exprType = d.getType(); - arrayDim = d.getArrayDim(); - className = d.getClassName(); - int var = getLocalVar(d); - - if (arrayDim > 0) - bytecode.addAload(var); - else - switch (exprType) { - case CLASS : - bytecode.addAload(var); - break; - case LONG : - bytecode.addLload(var); - break; - case FLOAT : - bytecode.addFload(var); - break; - case DOUBLE : - bytecode.addDload(var); - break; - default : // BOOLEAN, BYTE, CHAR, SHORT, INT - bytecode.addIload(var); - break; - } - } - - public void atKeyword(Keyword k) throws CompileError { - arrayDim = 0; - int token = k.get(); - switch (token) { - case TRUE : - bytecode.addIconst(1); - exprType = BOOLEAN; - break; - case FALSE : - bytecode.addIconst(0); - exprType = BOOLEAN; - break; - case NULL : - bytecode.addOpcode(ACONST_NULL); - exprType = NULL; - break; - case THIS : - case SUPER : - if (inStaticMethod) - throw new CompileError("not-available: " - + (token == THIS ? "this" : "super")); - - bytecode.addAload(0); - exprType = CLASS; - if (token == THIS) - className = getThisName(); - else - className = getSuperName(); - break; - default : - fatal(); - } - } - - public void atStringL(StringL s) throws CompileError { - exprType = CLASS; - arrayDim = 0; - className = jvmJavaLangString; - bytecode.addLdc(s.get()); - } - - public void atIntConst(IntConst i) throws CompileError { - arrayDim = 0; - long value = i.get(); - int type = i.getType(); - if (type == IntConstant || type == CharConstant) { - exprType = (type == IntConstant ? INT : CHAR); - bytecode.addIconst((int)value); - } - else { - exprType = LONG; - bytecode.addLconst(value); - } - } - - public void atDoubleConst(DoubleConst d) throws CompileError { - arrayDim = 0; - if (d.getType() == DoubleConstant) { - exprType = DOUBLE; - bytecode.addDconst(d.get()); - } - else { - exprType = FLOAT; - bytecode.addFconst((float)d.get()); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/CompileError.java b/src/com/wenshuo/agent/javassist/compiler/CompileError.java deleted file mode 100644 index 7944620..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/CompileError.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.NotFoundException; - -public class CompileError extends Exception { - private Lex lex; - private String reason; - - public CompileError(String s, Lex l) { - reason = s; - lex = l; - } - - public CompileError(String s) { - reason = s; - lex = null; - } - - public CompileError(CannotCompileException e) { - this(e.getReason()); - } - - public CompileError(NotFoundException e) { - this("cannot find " + e.getMessage()); - } - - public Lex getLex() { return lex; } - - public String getMessage() { - return reason; - } - - public String toString() { - return "compile error: " + reason; - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/Javac.java b/src/com/wenshuo/agent/javassist/compiler/Javac.java deleted file mode 100644 index cfbeb4b..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/Javac.java +++ /dev/null @@ -1,610 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtPrimitiveType; -import com.wenshuo.agent.javassist.CtMember; -import com.wenshuo.agent.javassist.CtField; -import com.wenshuo.agent.javassist.CtBehavior; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.CtConstructor; -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.Modifier; -import com.wenshuo.agent.javassist.bytecode.Bytecode; -import com.wenshuo.agent.javassist.bytecode.CodeAttribute; -import com.wenshuo.agent.javassist.bytecode.LocalVariableAttribute; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.Opcode; -import com.wenshuo.agent.javassist.NotFoundException; - -import com.wenshuo.agent.javassist.compiler.ast.*; - -public class Javac { - JvstCodeGen gen; - SymbolTable stable; - private Bytecode bytecode; - - public static final String param0Name = "$0"; - public static final String resultVarName = "$_"; - public static final String proceedName = "$proceed"; - - /** - * Constructs a compiler. - * - * @param thisClass the class that a compiled method/field - * belongs to. - */ - public Javac(CtClass thisClass) { - this(new Bytecode(thisClass.getClassFile2().getConstPool(), 0, 0), - thisClass); - } - - /** - * Constructs a compiler. - * The produced bytecode is stored in the Bytecode object - * specified by b. - * - * @param thisClass the class that a compiled method/field - * belongs to. - */ - public Javac(Bytecode b, CtClass thisClass) { - gen = new JvstCodeGen(b, thisClass, thisClass.getClassPool()); - stable = new SymbolTable(); - bytecode = b; - } - - /** - * Returns the produced bytecode. - */ - public Bytecode getBytecode() { return bytecode; } - - /** - * Compiles a method, constructor, or field declaration - * to a class. - * A field declaration can declare only one field. - * - *

In a method or constructor body, $0, $1, ... and $_ - * are not available. - * - * @return a CtMethod, CtConstructor, - * or CtField object. - * @see #recordProceed(String,String) - */ - public CtMember compile(String src) throws CompileError { - Parser p = new Parser(new Lex(src)); - ASTList mem = p.parseMember1(stable); - try { - if (mem instanceof FieldDecl) - return compileField((FieldDecl)mem); - else { - CtBehavior cb = compileMethod(p, (MethodDecl)mem); - CtClass decl = cb.getDeclaringClass(); - cb.getMethodInfo2() - .rebuildStackMapIf6(decl.getClassPool(), - decl.getClassFile2()); - return cb; - } - } - catch (BadBytecode bb) { - throw new CompileError(bb.getMessage()); - } - catch (CannotCompileException e) { - throw new CompileError(e.getMessage()); - } - } - - public static class CtFieldWithInit extends CtField { - private ASTree init; - - CtFieldWithInit(CtClass type, String name, CtClass declaring) - throws CannotCompileException - { - super(type, name, declaring); - init = null; - } - - protected void setInit(ASTree i) { init = i; } - - protected ASTree getInitAST() { - return init; - } - } - - private CtField compileField(FieldDecl fd) - throws CompileError, CannotCompileException - { - CtFieldWithInit f; - Declarator d = fd.getDeclarator(); - f = new CtFieldWithInit(gen.resolver.lookupClass(d), - d.getVariable().get(), gen.getThisClass()); - f.setModifiers(MemberResolver.getModifiers(fd.getModifiers())); - if (fd.getInit() != null) - f.setInit(fd.getInit()); - - return f; - } - - private CtBehavior compileMethod(Parser p, MethodDecl md) - throws CompileError - { - int mod = MemberResolver.getModifiers(md.getModifiers()); - CtClass[] plist = gen.makeParamList(md); - CtClass[] tlist = gen.makeThrowsList(md); - recordParams(plist, Modifier.isStatic(mod)); - md = p.parseMethod2(stable, md); - try { - if (md.isConstructor()) { - CtConstructor cons = new CtConstructor(plist, - gen.getThisClass()); - cons.setModifiers(mod); - md.accept(gen); - cons.getMethodInfo().setCodeAttribute( - bytecode.toCodeAttribute()); - cons.setExceptionTypes(tlist); - return cons; - } - else { - Declarator r = md.getReturn(); - CtClass rtype = gen.resolver.lookupClass(r); - recordReturnType(rtype, false); - CtMethod method = new CtMethod(rtype, r.getVariable().get(), - plist, gen.getThisClass()); - method.setModifiers(mod); - gen.setThisMethod(method); - md.accept(gen); - if (md.getBody() != null) - method.getMethodInfo().setCodeAttribute( - bytecode.toCodeAttribute()); - else - method.setModifiers(mod | Modifier.ABSTRACT); - - method.setExceptionTypes(tlist); - return method; - } - } - catch (NotFoundException e) { - throw new CompileError(e.toString()); - } - } - - /** - * Compiles a method (or constructor) body. - * - * @param src a single statement or a block. - * If null, this method produces a body returning zero or null. - */ - public Bytecode compileBody(CtBehavior method, String src) - throws CompileError - { - try { - int mod = method.getModifiers(); - recordParams(method.getParameterTypes(), Modifier.isStatic(mod)); - - CtClass rtype; - if (method instanceof CtMethod) { - gen.setThisMethod((CtMethod)method); - rtype = ((CtMethod)method).getReturnType(); - } - else - rtype = CtClass.voidType; - - recordReturnType(rtype, false); - boolean isVoid = rtype == CtClass.voidType; - - if (src == null) - makeDefaultBody(bytecode, rtype); - else { - Parser p = new Parser(new Lex(src)); - SymbolTable stb = new SymbolTable(stable); - Stmnt s = p.parseStatement(stb); - if (p.hasMore()) - throw new CompileError( - "the method/constructor body must be surrounded by {}"); - - boolean callSuper = false; - if (method instanceof CtConstructor) - callSuper = !((CtConstructor)method).isClassInitializer(); - - gen.atMethodBody(s, callSuper, isVoid); - } - - return bytecode; - } - catch (NotFoundException e) { - throw new CompileError(e.toString()); - } - } - - private static void makeDefaultBody(Bytecode b, CtClass type) { - int op; - int value; - if (type instanceof CtPrimitiveType) { - CtPrimitiveType pt = (CtPrimitiveType)type; - op = pt.getReturnOp(); - if (op == Opcode.DRETURN) - value = Opcode.DCONST_0; - else if (op == Opcode.FRETURN) - value = Opcode.FCONST_0; - else if (op == Opcode.LRETURN) - value = Opcode.LCONST_0; - else if (op == Opcode.RETURN) - value = Opcode.NOP; - else - value = Opcode.ICONST_0; - } - else { - op = Opcode.ARETURN; - value = Opcode.ACONST_NULL; - } - - if (value != Opcode.NOP) - b.addOpcode(value); - - b.addOpcode(op); - } - - /** - * Records local variables available at the specified program counter. - * If the LocalVariableAttribute is not available, this method does not - * record any local variable. It only returns false. - * - * @param pc program counter (>= 0) - * @return false if the CodeAttribute does not include a - * LocalVariableAttribute. - */ - public boolean recordLocalVariables(CodeAttribute ca, int pc) - throws CompileError - { - LocalVariableAttribute va - = (LocalVariableAttribute) - ca.getAttribute(LocalVariableAttribute.tag); - if (va == null) - return false; - - int n = va.tableLength(); - for (int i = 0; i < n; ++i) { - int start = va.startPc(i); - int len = va.codeLength(i); - if (start <= pc && pc < start + len) - gen.recordVariable(va.descriptor(i), va.variableName(i), - va.index(i), stable); - } - - return true; - } - - /** - * Records parameter names if the LocalVariableAttribute is available. - * It returns false unless the LocalVariableAttribute is available. - * - * @param numOfLocalVars the number of local variables used - * for storing the parameters. - * @return false if the CodeAttribute does not include a - * LocalVariableAttribute. - */ - public boolean recordParamNames(CodeAttribute ca, int numOfLocalVars) - throws CompileError - { - LocalVariableAttribute va - = (LocalVariableAttribute) - ca.getAttribute(LocalVariableAttribute.tag); - if (va == null) - return false; - - int n = va.tableLength(); - for (int i = 0; i < n; ++i) { - int index = va.index(i); - if (index < numOfLocalVars) - gen.recordVariable(va.descriptor(i), va.variableName(i), - index, stable); - } - - return true; - } - - - /** - * Makes variables $0 (this), $1, $2, ..., and $args represent method - * parameters. $args represents an array of all the parameters. - * It also makes $$ available as a parameter list of method call. - * - *

This must be called before calling compileStmnt() and - * compileExpr(). The correct value of - * isStatic must be recorded before compilation. - * maxLocals is updated to include $0,... - */ - public int recordParams(CtClass[] params, boolean isStatic) - throws CompileError - { - return gen.recordParams(params, isStatic, "$", "$args", "$$", stable); - } - - /** - * Makes variables $0, $1, $2, ..., and $args represent method - * parameters. $args represents an array of all the parameters. - * It also makes $$ available as a parameter list of method call. - * $0 can represent a local variable other than THIS (variable 0). - * $class is also made available. - * - *

This must be called before calling compileStmnt() and - * compileExpr(). The correct value of - * isStatic must be recorded before compilation. - * maxLocals is updated to include $0,... - * - * @param use0 true if $0 is used. - * @param varNo the register number of $0 (use0 is true) - * or $1 (otherwise). - * @param target the type of $0 (it can be null if use0 is false). - * It is used as the name of the type represented - * by $class. - * @param isStatic true if the method in which the compiled bytecode - * is embedded is static. - */ - public int recordParams(String target, CtClass[] params, - boolean use0, int varNo, boolean isStatic) - throws CompileError - { - return gen.recordParams(params, isStatic, "$", "$args", "$$", - use0, varNo, target, stable); - } - - /** - * Sets maxLocals to max. - * This method tells the compiler the local variables that have been - * allocated for the rest of the code. When the compiler needs - * new local variables, the local variables at the index max, - * max + 1, ... are assigned. - * - *

This method is indirectly called by recordParams. - */ - public void setMaxLocals(int max) { - gen.setMaxLocals(max); - } - - /** - * Prepares to use cast $r, $w, $_, and $type. - * $type is made to represent the specified return type. - * It also enables to write a return statement with a return value - * for void method. - * - *

If the return type is void, ($r) does nothing. - * The type of $_ is java.lang.Object. - * - * @param type the return type. - * @param useResultVar true if $_ is used. - * @return -1 or the variable index assigned to $_. - * @see #recordType(CtClass) - */ - public int recordReturnType(CtClass type, boolean useResultVar) - throws CompileError - { - gen.recordType(type); - return gen.recordReturnType(type, "$r", - (useResultVar ? resultVarName : null), stable); - } - - /** - * Prepares to use $type. Note that recordReturnType() overwrites - * the value of $type. - * - * @param t the type represented by $type. - */ - public void recordType(CtClass t) { - gen.recordType(t); - } - - /** - * Makes the given variable available. - * - * @param type variable type - * @param name variable name - */ - public int recordVariable(CtClass type, String name) - throws CompileError - { - return gen.recordVariable(type, name, stable); - } - - /** - * Prepares to use $proceed(). - * If the return type of $proceed() is void, null is pushed on the - * stack. - * - * @param target an expression specifying the target object. - * if null, "this" is the target. - * @param method the method name. - */ - public void recordProceed(String target, String method) - throws CompileError - { - Parser p = new Parser(new Lex(target)); - final ASTree texpr = p.parseExpression(stable); - final String m = method; - - ProceedHandler h = new ProceedHandler() { - public void doit(JvstCodeGen gen, Bytecode b, ASTList args) - throws CompileError - { - ASTree expr = new Member(m); - if (texpr != null) - expr = Expr.make('.', texpr, expr); - - expr = CallExpr.makeCall(expr, args); - gen.compileExpr(expr); - gen.addNullIfVoid(); - } - - public void setReturnType(JvstTypeChecker check, ASTList args) - throws CompileError - { - ASTree expr = new Member(m); - if (texpr != null) - expr = Expr.make('.', texpr, expr); - - expr = CallExpr.makeCall(expr, args); - expr.accept(check); - check.addNullIfVoid(); - } - }; - - gen.setProceedHandler(h, proceedName); - } - - /** - * Prepares to use $proceed() representing a static method. - * If the return type of $proceed() is void, null is pushed on the - * stack. - * - * @param targetClass the fully-qualified dot-separated name - * of the class declaring the method. - * @param method the method name. - */ - public void recordStaticProceed(String targetClass, String method) - throws CompileError - { - final String c = targetClass; - final String m = method; - - ProceedHandler h = new ProceedHandler() { - public void doit(JvstCodeGen gen, Bytecode b, ASTList args) - throws CompileError - { - Expr expr = Expr.make(TokenId.MEMBER, - new Symbol(c), new Member(m)); - expr = CallExpr.makeCall(expr, args); - gen.compileExpr(expr); - gen.addNullIfVoid(); - } - - public void setReturnType(JvstTypeChecker check, ASTList args) - throws CompileError - { - Expr expr = Expr.make(TokenId.MEMBER, - new Symbol(c), new Member(m)); - expr = CallExpr.makeCall(expr, args); - expr.accept(check); - check.addNullIfVoid(); - } - }; - - gen.setProceedHandler(h, proceedName); - } - - /** - * Prepares to use $proceed() representing a private/super's method. - * If the return type of $proceed() is void, null is pushed on the - * stack. This method is for methods invoked by INVOKESPECIAL. - * - * @param target an expression specifying the target object. - * if null, "this" is the target. - * @param classname the class name declaring the method. - * @param methodname the method name. - * @param descriptor the method descriptor. - */ - public void recordSpecialProceed(String target, String classname, - String methodname, String descriptor) - throws CompileError - { - Parser p = new Parser(new Lex(target)); - final ASTree texpr = p.parseExpression(stable); - final String cname = classname; - final String method = methodname; - final String desc = descriptor; - - ProceedHandler h = new ProceedHandler() { - public void doit(JvstCodeGen gen, Bytecode b, ASTList args) - throws CompileError - { - gen.compileInvokeSpecial(texpr, cname, method, desc, args); - } - - public void setReturnType(JvstTypeChecker c, ASTList args) - throws CompileError - { - c.compileInvokeSpecial(texpr, cname, method, desc, args); - } - - }; - - gen.setProceedHandler(h, proceedName); - } - - /** - * Prepares to use $proceed(). - */ - public void recordProceed(ProceedHandler h) { - gen.setProceedHandler(h, proceedName); - } - - /** - * Compiles a statement (or a block). - * recordParams() must be called before invoking - * this method. - * - *

Local variables that are not declared - * in the compiled source text might not be accessible within that - * source text. Fields and method parameters ($0, $1, ..) are available. - */ - public void compileStmnt(String src) throws CompileError { - Parser p = new Parser(new Lex(src)); - SymbolTable stb = new SymbolTable(stable); - while (p.hasMore()) { - Stmnt s = p.parseStatement(stb); - if (s != null) - s.accept(gen); - } - } - - /** - * Compiles an exression. recordParams() must be - * called before invoking this method. - * - *

Local variables are not accessible - * within the compiled source text. Fields and method parameters - * ($0, $1, ..) are available if recordParams() - * have been invoked. - */ - public void compileExpr(String src) throws CompileError { - ASTree e = parseExpr(src, stable); - compileExpr(e); - } - - /** - * Parsers an expression. - */ - public static ASTree parseExpr(String src, SymbolTable st) - throws CompileError - { - Parser p = new Parser(new Lex(src)); - return p.parseExpression(st); - } - - /** - * Compiles an exression. recordParams() must be - * called before invoking this method. - * - *

Local variables are not accessible - * within the compiled source text. Fields and method parameters - * ($0, $1, ..) are available if recordParams() - * have been invoked. - */ - public void compileExpr(ASTree e) throws CompileError { - if (e != null) - gen.compileExpr(e); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java b/src/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java deleted file mode 100644 index 7eb03ab..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java +++ /dev/null @@ -1,710 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.ast.*; - -/* Code generator accepting extended Java syntax for Javassist. - */ - -public class JvstCodeGen extends MemberCodeGen { - String paramArrayName = null; - String paramListName = null; - CtClass[] paramTypeList = null; - private int paramVarBase = 0; // variable index for $0 or $1. - private boolean useParam0 = false; // true if $0 is used. - private String param0Type = null; // JVM name - public static final String sigName = "$sig"; - public static final String dollarTypeName = "$type"; - public static final String clazzName = "$class"; - private CtClass dollarType = null; - CtClass returnType = null; - String returnCastName = null; - private String returnVarName = null; // null if $_ is not used. - public static final String wrapperCastName = "$w"; - String proceedName = null; - public static final String cflowName = "$cflow"; - ProceedHandler procHandler = null; // null if not used. - - public JvstCodeGen(Bytecode b, CtClass cc, ClassPool cp) { - super(b, cc, cp); - setTypeChecker(new JvstTypeChecker(cc, cp, this)); - } - - /* Index of $1. - */ - private int indexOfParam1() { - return paramVarBase + (useParam0 ? 1 : 0); - } - - /* Records a ProceedHandler obejct. - * - * @param name the name of the special method call. - * it is usually $proceed. - */ - public void setProceedHandler(ProceedHandler h, String name) { - proceedName = name; - procHandler = h; - } - - /* If the type of the expression compiled last is void, - * add ACONST_NULL and change exprType, arrayDim, className. - */ - public void addNullIfVoid() { - if (exprType == VOID) { - bytecode.addOpcode(ACONST_NULL); - exprType = CLASS; - arrayDim = 0; - className = jvmJavaLangObject; - } - } - - /* To support $args, $sig, and $type. - * $args is an array of parameter list. - */ - public void atMember(Member mem) throws CompileError { - String name = mem.get(); - if (name.equals(paramArrayName)) { - compileParameterList(bytecode, paramTypeList, indexOfParam1()); - exprType = CLASS; - arrayDim = 1; - className = jvmJavaLangObject; - } - else if (name.equals(sigName)) { - bytecode.addLdc(Descriptor.ofMethod(returnType, paramTypeList)); - bytecode.addInvokestatic("javassist/runtime/Desc", "getParams", - "(Ljava/lang/String;)[Ljava/lang/Class;"); - exprType = CLASS; - arrayDim = 1; - className = "java/lang/Class"; - } - else if (name.equals(dollarTypeName)) { - if (dollarType == null) - throw new CompileError(dollarTypeName + " is not available"); - - bytecode.addLdc(Descriptor.of(dollarType)); - callGetType("getType"); - } - else if (name.equals(clazzName)) { - if (param0Type == null) - throw new CompileError(clazzName + " is not available"); - - bytecode.addLdc(param0Type); - callGetType("getClazz"); - } - else - super.atMember(mem); - } - - private void callGetType(String method) { - bytecode.addInvokestatic("javassist/runtime/Desc", method, - "(Ljava/lang/String;)Ljava/lang/Class;"); - exprType = CLASS; - arrayDim = 0; - className = "java/lang/Class"; - } - - protected void atFieldAssign(Expr expr, int op, ASTree left, - ASTree right, boolean doDup) throws CompileError - { - if (left instanceof Member - && ((Member)left).get().equals(paramArrayName)) { - if (op != '=') - throw new CompileError("bad operator for " + paramArrayName); - - right.accept(this); - if (arrayDim != 1 || exprType != CLASS) - throw new CompileError("invalid type for " + paramArrayName); - - atAssignParamList(paramTypeList, bytecode); - if (!doDup) - bytecode.addOpcode(POP); - } - else - super.atFieldAssign(expr, op, left, right, doDup); - } - - protected void atAssignParamList(CtClass[] params, Bytecode code) - throws CompileError - { - if (params == null) - return; - - int varNo = indexOfParam1(); - int n = params.length; - for (int i = 0; i < n; ++i) { - code.addOpcode(DUP); - code.addIconst(i); - code.addOpcode(AALOAD); - compileUnwrapValue(params[i], code); - code.addStore(varNo, params[i]); - varNo += is2word(exprType, arrayDim) ? 2 : 1; - } - } - - public void atCastExpr(CastExpr expr) throws CompileError { - ASTList classname = expr.getClassName(); - if (classname != null && expr.getArrayDim() == 0) { - ASTree p = classname.head(); - if (p instanceof Symbol && classname.tail() == null) { - String typename = ((Symbol)p).get(); - if (typename.equals(returnCastName)) { - atCastToRtype(expr); - return; - } - else if (typename.equals(wrapperCastName)) { - atCastToWrapper(expr); - return; - } - } - } - - super.atCastExpr(expr); - } - - /** - * Inserts a cast operator to the return type. - * If the return type is void, this does nothing. - */ - protected void atCastToRtype(CastExpr expr) throws CompileError { - expr.getOprand().accept(this); - if (exprType == VOID || isRefType(exprType) || arrayDim > 0) - compileUnwrapValue(returnType, bytecode); - else if (returnType instanceof CtPrimitiveType) { - CtPrimitiveType pt = (CtPrimitiveType)returnType; - int destType = MemberResolver.descToType(pt.getDescriptor()); - atNumCastExpr(exprType, destType); - exprType = destType; - arrayDim = 0; - className = null; - } - else - throw new CompileError("invalid cast"); - } - - protected void atCastToWrapper(CastExpr expr) throws CompileError { - expr.getOprand().accept(this); - if (isRefType(exprType) || arrayDim > 0) - return; // Object type. do nothing. - - CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); - if (clazz instanceof CtPrimitiveType) { - CtPrimitiveType pt = (CtPrimitiveType)clazz; - String wrapper = pt.getWrapperName(); - bytecode.addNew(wrapper); // new - bytecode.addOpcode(DUP); // dup - if (pt.getDataSize() > 1) - bytecode.addOpcode(DUP2_X2); // dup2_x2 - else - bytecode.addOpcode(DUP2_X1); // dup2_x1 - - bytecode.addOpcode(POP2); // pop2 - bytecode.addInvokespecial(wrapper, "", - "(" + pt.getDescriptor() + ")V"); - // invokespecial - exprType = CLASS; - arrayDim = 0; - className = jvmJavaLangObject; - } - } - - /* Delegates to a ProcHandler object if the method call is - * $proceed(). It may process $cflow(). - */ - public void atCallExpr(CallExpr expr) throws CompileError { - ASTree method = expr.oprand1(); - if (method instanceof Member) { - String name = ((Member)method).get(); - if (procHandler != null && name.equals(proceedName)) { - procHandler.doit(this, bytecode, (ASTList)expr.oprand2()); - return; - } - else if (name.equals(cflowName)) { - atCflow((ASTList)expr.oprand2()); - return; - } - } - - super.atCallExpr(expr); - } - - /* To support $cflow(). - */ - protected void atCflow(ASTList cname) throws CompileError { - StringBuffer sbuf = new StringBuffer(); - if (cname == null || cname.tail() != null) - throw new CompileError("bad " + cflowName); - - makeCflowName(sbuf, cname.head()); - String name = sbuf.toString(); - Object[] names = resolver.getClassPool().lookupCflow(name); - if (names == null) - throw new CompileError("no such " + cflowName + ": " + name); - - bytecode.addGetstatic((String)names[0], (String)names[1], - "Ljavassist/runtime/Cflow;"); - bytecode.addInvokevirtual("javassist.runtime.Cflow", - "value", "()I"); - exprType = INT; - arrayDim = 0; - className = null; - } - - /* Syntax: - * - * : $cflow '(' ')' - * : ('.' )* - */ - private static void makeCflowName(StringBuffer sbuf, ASTree name) - throws CompileError - { - if (name instanceof Symbol) { - sbuf.append(((Symbol)name).get()); - return; - } - else if (name instanceof Expr) { - Expr expr = (Expr)name; - if (expr.getOperator() == '.') { - makeCflowName(sbuf, expr.oprand1()); - sbuf.append('.'); - makeCflowName(sbuf, expr.oprand2()); - return; - } - } - - throw new CompileError("bad " + cflowName); - } - - /* To support $$. ($$) is equivalent to ($1, ..., $n). - * It can be used only as a parameter list of method call. - */ - public boolean isParamListName(ASTList args) { - if (paramTypeList != null - && args != null && args.tail() == null) { - ASTree left = args.head(); - return (left instanceof Member - && ((Member)left).get().equals(paramListName)); - } - else - return false; - } - - /* - public int getMethodArgsLength(ASTList args) { - if (!isParamListName(args)) - return super.getMethodArgsLength(args); - - return paramTypeList.length; - } - */ - - public int getMethodArgsLength(ASTList args) { - String pname = paramListName; - int n = 0; - while (args != null) { - ASTree a = args.head(); - if (a instanceof Member && ((Member)a).get().equals(pname)) { - if (paramTypeList != null) - n += paramTypeList.length; - } - else - ++n; - - args = args.tail(); - } - - return n; - } - - public void atMethodArgs(ASTList args, int[] types, int[] dims, - String[] cnames) throws CompileError { - CtClass[] params = paramTypeList; - String pname = paramListName; - int i = 0; - while (args != null) { - ASTree a = args.head(); - if (a instanceof Member && ((Member)a).get().equals(pname)) { - if (params != null) { - int n = params.length; - int regno = indexOfParam1(); - for (int k = 0; k < n; ++k) { - CtClass p = params[k]; - regno += bytecode.addLoad(regno, p); - setType(p); - types[i] = exprType; - dims[i] = arrayDim; - cnames[i] = className; - ++i; - } - } - } - else { - a.accept(this); - types[i] = exprType; - dims[i] = arrayDim; - cnames[i] = className; - ++i; - } - - args = args.tail(); - } - } - - /* - public void atMethodArgs(ASTList args, int[] types, int[] dims, - String[] cnames) throws CompileError { - if (!isParamListName(args)) { - super.atMethodArgs(args, types, dims, cnames); - return; - } - - CtClass[] params = paramTypeList; - if (params == null) - return; - - int n = params.length; - int regno = indexOfParam1(); - for (int i = 0; i < n; ++i) { - CtClass p = params[i]; - regno += bytecode.addLoad(regno, p); - setType(p); - types[i] = exprType; - dims[i] = arrayDim; - cnames[i] = className; - } - } - */ - - /* called by Javac#recordSpecialProceed(). - */ - void compileInvokeSpecial(ASTree target, String classname, - String methodname, String descriptor, - ASTList args) - throws CompileError - { - target.accept(this); - int nargs = getMethodArgsLength(args); - atMethodArgs(args, new int[nargs], new int[nargs], - new String[nargs]); - bytecode.addInvokespecial(classname, methodname, descriptor); - setReturnType(descriptor, false, false); - addNullIfVoid(); - } - - /* - * Makes it valid to write "return ;" for a void method. - */ - protected void atReturnStmnt(Stmnt st) throws CompileError { - ASTree result = st.getLeft(); - if (result != null && returnType == CtClass.voidType) { - compileExpr(result); - if (is2word(exprType, arrayDim)) - bytecode.addOpcode(POP2); - else if (exprType != VOID) - bytecode.addOpcode(POP); - - result = null; - } - - atReturnStmnt2(result); - } - - /** - * Makes a cast to the return type ($r) available. - * It also enables $_. - * - *

If the return type is void, ($r) does nothing. - * The type of $_ is java.lang.Object. - * - * @param resultName null if $_ is not used. - * @return -1 or the variable index assigned to $_. - */ - public int recordReturnType(CtClass type, String castName, - String resultName, SymbolTable tbl) throws CompileError - { - returnType = type; - returnCastName = castName; - returnVarName = resultName; - if (resultName == null) - return -1; - else { - int varNo = getMaxLocals(); - int locals = varNo + recordVar(type, resultName, varNo, tbl); - setMaxLocals(locals); - return varNo; - } - } - - /** - * Makes $type available. - */ - public void recordType(CtClass t) { - dollarType = t; - } - - /** - * Makes method parameters $0, $1, ..., $args, $$, and $class available. - * $0 is equivalent to THIS if the method is not static. Otherwise, - * if the method is static, then $0 is not available. - */ - public int recordParams(CtClass[] params, boolean isStatic, - String prefix, String paramVarName, - String paramsName, SymbolTable tbl) - throws CompileError - { - return recordParams(params, isStatic, prefix, paramVarName, - paramsName, !isStatic, 0, getThisName(), tbl); - } - - /** - * Makes method parameters $0, $1, ..., $args, $$, and $class available. - * $0 is available only if use0 is true. It might not be equivalent - * to THIS. - * - * @param params the parameter types (the types of $1, $2, ..) - * @param prefix it must be "$" (the first letter of $0, $1, ...) - * @param paramVarName it must be "$args" - * @param paramsName it must be "$$" - * @param use0 true if $0 is used. - * @param paramBase the register number of $0 (use0 is true) - * or $1 (otherwise). - * @param target the class of $0. If use0 is false, target - * can be null. The value of "target" is also used - * as the name of the type represented by $class. - * @param isStatic true if the method in which the compiled bytecode - * is embedded is static. - */ - public int recordParams(CtClass[] params, boolean isStatic, - String prefix, String paramVarName, - String paramsName, boolean use0, - int paramBase, String target, - SymbolTable tbl) - throws CompileError - { - int varNo; - - paramTypeList = params; - paramArrayName = paramVarName; - paramListName = paramsName; - paramVarBase = paramBase; - useParam0 = use0; - - if (target != null) - param0Type = MemberResolver.jvmToJavaName(target); - - inStaticMethod = isStatic; - varNo = paramBase; - if (use0) { - String varName = prefix + "0"; - Declarator decl - = new Declarator(CLASS, MemberResolver.javaToJvmName(target), - 0, varNo++, new Symbol(varName)); - tbl.append(varName, decl); - } - - for (int i = 0; i < params.length; ++i) - varNo += recordVar(params[i], prefix + (i + 1), varNo, tbl); - - if (getMaxLocals() < varNo) - setMaxLocals(varNo); - - return varNo; - } - - /** - * Makes the given variable name available. - * - * @param type variable type - * @param varName variable name - */ - public int recordVariable(CtClass type, String varName, SymbolTable tbl) - throws CompileError - { - if (varName == null) - return -1; - else { - int varNo = getMaxLocals(); - int locals = varNo + recordVar(type, varName, varNo, tbl); - setMaxLocals(locals); - return varNo; - } - } - - private int recordVar(CtClass cc, String varName, int varNo, - SymbolTable tbl) throws CompileError - { - if (cc == CtClass.voidType) { - exprType = CLASS; - arrayDim = 0; - className = jvmJavaLangObject; - } - else - setType(cc); - - Declarator decl - = new Declarator(exprType, className, arrayDim, - varNo, new Symbol(varName)); - tbl.append(varName, decl); - return is2word(exprType, arrayDim) ? 2 : 1; - } - - /** - * Makes the given variable name available. - * - * @param typeDesc the type descriptor of the variable - * @param varName variable name - * @param varNo an index into the local variable array - */ - public void recordVariable(String typeDesc, String varName, int varNo, - SymbolTable tbl) throws CompileError - { - char c; - int dim = 0; - while ((c = typeDesc.charAt(dim)) == '[') - ++dim; - - int type = MemberResolver.descToType(c); - String cname = null; - if (type == CLASS) { - if (dim == 0) - cname = typeDesc.substring(1, typeDesc.length() - 1); - else - cname = typeDesc.substring(dim + 1, typeDesc.length() - 1); - } - - Declarator decl - = new Declarator(type, cname, dim, varNo, new Symbol(varName)); - tbl.append(varName, decl); - } - - /* compileParameterList() returns the stack size used - * by the produced code. - * - * This method correctly computes the max_stack value. - * - * @param regno the index of the local variable in which - * the first argument is received. - * (0: static method, 1: regular method.) - */ - public static int compileParameterList(Bytecode code, - CtClass[] params, int regno) { - if (params == null) { - code.addIconst(0); // iconst_0 - code.addAnewarray(javaLangObject); // anewarray Object - return 1; - } - else { - CtClass[] args = new CtClass[1]; - int n = params.length; - code.addIconst(n); // iconst_ - code.addAnewarray(javaLangObject); // anewarray Object - for (int i = 0; i < n; ++i) { - code.addOpcode(Bytecode.DUP); // dup - code.addIconst(i); // iconst_ - if (params[i].isPrimitive()) { - CtPrimitiveType pt = (CtPrimitiveType)params[i]; - String wrapper = pt.getWrapperName(); - code.addNew(wrapper); // new - code.addOpcode(Bytecode.DUP); // dup - int s = code.addLoad(regno, pt); // ?load - regno += s; - args[0] = pt; - code.addInvokespecial(wrapper, "", - Descriptor.ofMethod(CtClass.voidType, args)); - // invokespecial - } - else { - code.addAload(regno); // aload - ++regno; - } - - code.addOpcode(Bytecode.AASTORE); // aastore - } - - return 8; - } - } - - protected void compileUnwrapValue(CtClass type, Bytecode code) - throws CompileError - { - if (type == CtClass.voidType) { - addNullIfVoid(); - return; - } - - if (exprType == VOID) - throw new CompileError("invalid type for " + returnCastName); - - if (type instanceof CtPrimitiveType) { - CtPrimitiveType pt = (CtPrimitiveType)type; - // pt is not voidType. - String wrapper = pt.getWrapperName(); - code.addCheckcast(wrapper); - code.addInvokevirtual(wrapper, pt.getGetMethodName(), - pt.getGetMethodDescriptor()); - setType(type); - } - else { - code.addCheckcast(type); - setType(type); - } - } - - /* Sets exprType, arrayDim, and className; - * If type is void, then this method does nothing. - */ - public void setType(CtClass type) throws CompileError { - setType(type, 0); - } - - private void setType(CtClass type, int dim) throws CompileError { - if (type.isPrimitive()) { - CtPrimitiveType pt = (CtPrimitiveType)type; - exprType = MemberResolver.descToType(pt.getDescriptor()); - arrayDim = dim; - className = null; - } - else if (type.isArray()) - try { - setType(type.getComponentType(), dim + 1); - } - catch (NotFoundException e) { - throw new CompileError("undefined type: " + type.getName()); - } - else { - exprType = CLASS; - arrayDim = dim; - className = MemberResolver.javaToJvmName(type.getName()); - } - } - - /* Performs implicit coercion from exprType to type. - */ - public void doNumCast(CtClass type) throws CompileError { - if (arrayDim == 0 && !isRefType(exprType)) - if (type instanceof CtPrimitiveType) { - CtPrimitiveType pt = (CtPrimitiveType)type; - atNumCastExpr(exprType, - MemberResolver.descToType(pt.getDescriptor())); - } - else - throw new CompileError("type mismatch"); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java b/src/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java deleted file mode 100644 index a28aaad..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.compiler.ast.*; - -/* Type checker accepting extended Java syntax for Javassist. - */ - -public class JvstTypeChecker extends TypeChecker { - private JvstCodeGen codeGen; - - public JvstTypeChecker(CtClass cc, ClassPool cp, JvstCodeGen gen) { - super(cc, cp); - codeGen = gen; - } - - /* If the type of the expression compiled last is void, - * add ACONST_NULL and change exprType, arrayDim, className. - */ - public void addNullIfVoid() { - if (exprType == VOID) { - exprType = CLASS; - arrayDim = 0; - className = jvmJavaLangObject; - } - } - - /* To support $args, $sig, and $type. - * $args is an array of parameter list. - */ - public void atMember(Member mem) throws CompileError { - String name = mem.get(); - if (name.equals(codeGen.paramArrayName)) { - exprType = CLASS; - arrayDim = 1; - className = jvmJavaLangObject; - } - else if (name.equals(JvstCodeGen.sigName)) { - exprType = CLASS; - arrayDim = 1; - className = "java/lang/Class"; - } - else if (name.equals(JvstCodeGen.dollarTypeName) - || name.equals(JvstCodeGen.clazzName)) { - exprType = CLASS; - arrayDim = 0; - className = "java/lang/Class"; - } - else - super.atMember(mem); - } - - protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) - throws CompileError - { - if (left instanceof Member - && ((Member)left).get().equals(codeGen.paramArrayName)) { - right.accept(this); - CtClass[] params = codeGen.paramTypeList; - if (params == null) - return; - - int n = params.length; - for (int i = 0; i < n; ++i) - compileUnwrapValue(params[i]); - } - else - super.atFieldAssign(expr, op, left, right); - } - - public void atCastExpr(CastExpr expr) throws CompileError { - ASTList classname = expr.getClassName(); - if (classname != null && expr.getArrayDim() == 0) { - ASTree p = classname.head(); - if (p instanceof Symbol && classname.tail() == null) { - String typename = ((Symbol)p).get(); - if (typename.equals(codeGen.returnCastName)) { - atCastToRtype(expr); - return; - } - else if (typename.equals(JvstCodeGen.wrapperCastName)) { - atCastToWrapper(expr); - return; - } - } - } - - super.atCastExpr(expr); - } - - /** - * Inserts a cast operator to the return type. - * If the return type is void, this does nothing. - */ - protected void atCastToRtype(CastExpr expr) throws CompileError { - CtClass returnType = codeGen.returnType; - expr.getOprand().accept(this); - if (exprType == VOID || CodeGen.isRefType(exprType) || arrayDim > 0) - compileUnwrapValue(returnType); - else if (returnType instanceof CtPrimitiveType) { - CtPrimitiveType pt = (CtPrimitiveType)returnType; - int destType = MemberResolver.descToType(pt.getDescriptor()); - exprType = destType; - arrayDim = 0; - className = null; - } - } - - protected void atCastToWrapper(CastExpr expr) throws CompileError { - expr.getOprand().accept(this); - if (CodeGen.isRefType(exprType) || arrayDim > 0) - return; // Object type. do nothing. - - CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); - if (clazz instanceof CtPrimitiveType) { - exprType = CLASS; - arrayDim = 0; - className = jvmJavaLangObject; - } - } - - /* Delegates to a ProcHandler object if the method call is - * $proceed(). It may process $cflow(). - */ - public void atCallExpr(CallExpr expr) throws CompileError { - ASTree method = expr.oprand1(); - if (method instanceof Member) { - String name = ((Member)method).get(); - if (codeGen.procHandler != null - && name.equals(codeGen.proceedName)) { - codeGen.procHandler.setReturnType(this, - (ASTList)expr.oprand2()); - return; - } - else if (name.equals(JvstCodeGen.cflowName)) { - atCflow((ASTList)expr.oprand2()); - return; - } - } - - super.atCallExpr(expr); - } - - /* To support $cflow(). - */ - protected void atCflow(ASTList cname) throws CompileError { - exprType = INT; - arrayDim = 0; - className = null; - } - - /* To support $$. ($$) is equivalent to ($1, ..., $n). - * It can be used only as a parameter list of method call. - */ - public boolean isParamListName(ASTList args) { - if (codeGen.paramTypeList != null - && args != null && args.tail() == null) { - ASTree left = args.head(); - return (left instanceof Member - && ((Member)left).get().equals(codeGen.paramListName)); - } - else - return false; - } - - public int getMethodArgsLength(ASTList args) { - String pname = codeGen.paramListName; - int n = 0; - while (args != null) { - ASTree a = args.head(); - if (a instanceof Member && ((Member)a).get().equals(pname)) { - if (codeGen.paramTypeList != null) - n += codeGen.paramTypeList.length; - } - else - ++n; - - args = args.tail(); - } - - return n; - } - - public void atMethodArgs(ASTList args, int[] types, int[] dims, - String[] cnames) throws CompileError { - CtClass[] params = codeGen.paramTypeList; - String pname = codeGen.paramListName; - int i = 0; - while (args != null) { - ASTree a = args.head(); - if (a instanceof Member && ((Member)a).get().equals(pname)) { - if (params != null) { - int n = params.length; - for (int k = 0; k < n; ++k) { - CtClass p = params[k]; - setType(p); - types[i] = exprType; - dims[i] = arrayDim; - cnames[i] = className; - ++i; - } - } - } - else { - a.accept(this); - types[i] = exprType; - dims[i] = arrayDim; - cnames[i] = className; - ++i; - } - - args = args.tail(); - } - } - - /* called by Javac#recordSpecialProceed(). - */ - void compileInvokeSpecial(ASTree target, String classname, - String methodname, String descriptor, - ASTList args) - throws CompileError - { - target.accept(this); - int nargs = getMethodArgsLength(args); - atMethodArgs(args, new int[nargs], new int[nargs], - new String[nargs]); - setReturnType(descriptor); - addNullIfVoid(); - } - - protected void compileUnwrapValue(CtClass type) throws CompileError - { - if (type == CtClass.voidType) - addNullIfVoid(); - else - setType(type); - } - - /* Sets exprType, arrayDim, and className; - * If type is void, then this method does nothing. - */ - public void setType(CtClass type) throws CompileError { - setType(type, 0); - } - - private void setType(CtClass type, int dim) throws CompileError { - if (type.isPrimitive()) { - CtPrimitiveType pt = (CtPrimitiveType)type; - exprType = MemberResolver.descToType(pt.getDescriptor()); - arrayDim = dim; - className = null; - } - else if (type.isArray()) - try { - setType(type.getComponentType(), dim + 1); - } - catch (NotFoundException e) { - throw new CompileError("undefined type: " + type.getName()); - } - else { - exprType = CLASS; - arrayDim = dim; - className = MemberResolver.javaToJvmName(type.getName()); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/KeywordTable.java b/src/com/wenshuo/agent/javassist/compiler/KeywordTable.java deleted file mode 100644 index 8f4aa18..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/KeywordTable.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -public final class KeywordTable extends java.util.HashMap { - public KeywordTable() { super(); } - - public int lookup(String name) { - Object found = get(name); - if (found == null) - return -1; - else - return ((Integer)found).intValue(); - } - - public void append(String name, int t) { - put(name, new Integer(t)); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/Lex.java b/src/com/wenshuo/agent/javassist/compiler/Lex.java deleted file mode 100644 index f83e802..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/Lex.java +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -class Token { - public Token next = null; - public int tokenId; - - public long longValue; - public double doubleValue; - public String textValue; -} - -public class Lex implements TokenId { - private int lastChar; - private StringBuffer textBuffer; - private Token currentToken; - private Token lookAheadTokens; - - private String input; - private int position, maxlen, lineNumber; - - /** - * Constructs a lexical analyzer. - */ - public Lex(String s) { - lastChar = -1; - textBuffer = new StringBuffer(); - currentToken = new Token(); - lookAheadTokens = null; - - input = s; - position = 0; - maxlen = s.length(); - lineNumber = 0; - } - - public int get() { - if (lookAheadTokens == null) - return get(currentToken); - else { - Token t; - currentToken = t = lookAheadTokens; - lookAheadTokens = lookAheadTokens.next; - return t.tokenId; - } - } - - /** - * Looks at the next token. - */ - public int lookAhead() { - return lookAhead(0); - } - - public int lookAhead(int i) { - Token tk = lookAheadTokens; - if (tk == null) { - lookAheadTokens = tk = currentToken; // reuse an object! - tk.next = null; - get(tk); - } - - for (; i-- > 0; tk = tk.next) - if (tk.next == null) { - Token tk2; - tk.next = tk2 = new Token(); - get(tk2); - } - - currentToken = tk; - return tk.tokenId; - } - - public String getString() { - return currentToken.textValue; - } - - public long getLong() { - return currentToken.longValue; - } - - public double getDouble() { - return currentToken.doubleValue; - } - - private int get(Token token) { - int t; - do { - t = readLine(token); - } while (t == '\n'); - token.tokenId = t; - return t; - } - - private int readLine(Token token) { - int c = getNextNonWhiteChar(); - if(c < 0) - return c; - else if(c == '\n') { - ++lineNumber; - return '\n'; - } - else if (c == '\'') - return readCharConst(token); - else if (c == '"') - return readStringL(token); - else if ('0' <= c && c <= '9') - return readNumber(c, token); - else if(c == '.'){ - c = getc(); - if ('0' <= c && c <= '9') { - StringBuffer tbuf = textBuffer; - tbuf.setLength(0); - tbuf.append('.'); - return readDouble(tbuf, c, token); - } - else{ - ungetc(c); - return readSeparator('.'); - } - } - else if (Character.isJavaIdentifierStart((char)c)) - return readIdentifier(c, token); - else - return readSeparator(c); - } - - private int getNextNonWhiteChar() { - int c; - do { - c = getc(); - if (c == '/') { - c = getc(); - if (c == '/') - do { - c = getc(); - } while (c != '\n' && c != '\r' && c != -1); - else if (c == '*') - while (true) { - c = getc(); - if (c == -1) - break; - else if (c == '*') - if ((c = getc()) == '/') { - c = ' '; - break; - } - else - ungetc(c); - } - else { - ungetc(c); - c = '/'; - } - } - } while(isBlank(c)); - return c; - } - - private int readCharConst(Token token) { - int c; - int value = 0; - while ((c = getc()) != '\'') - if (c == '\\') - value = readEscapeChar(); - else if (c < 0x20) { - if (c == '\n') - ++lineNumber; - - return BadToken; - } - else - value = c; - - token.longValue = value; - return CharConstant; - } - - private int readEscapeChar() { - int c = getc(); - if (c == 'n') - c = '\n'; - else if (c == 't') - c = '\t'; - else if (c == 'r') - c = '\r'; - else if (c == 'f') - c = '\f'; - else if (c == '\n') - ++lineNumber; - - return c; - } - - private int readStringL(Token token) { - int c; - StringBuffer tbuf = textBuffer; - tbuf.setLength(0); - for (;;) { - while ((c = getc()) != '"') { - if (c == '\\') - c = readEscapeChar(); - else if (c == '\n' || c < 0) { - ++lineNumber; - return BadToken; - } - - tbuf.append((char)c); - } - - for (;;) { - c = getc(); - if (c == '\n') - ++lineNumber; - else if (!isBlank(c)) - break; - } - - if (c != '"') { - ungetc(c); - break; - } - } - - token.textValue = tbuf.toString(); - return StringL; - } - - private int readNumber(int c, Token token) { - long value = 0; - int c2 = getc(); - if (c == '0') - if (c2 == 'X' || c2 == 'x') - for (;;) { - c = getc(); - if ('0' <= c && c <= '9') - value = value * 16 + (long)(c - '0'); - else if ('A' <= c && c <= 'F') - value = value * 16 + (long)(c - 'A' + 10); - else if ('a' <= c && c <= 'f') - value = value * 16 + (long)(c - 'a' + 10); - else { - token.longValue = value; - if (c == 'L' || c == 'l') - return LongConstant; - else { - ungetc(c); - return IntConstant; - } - } - } - else if ('0' <= c2 && c2 <= '7') { - value = c2 - '0'; - for (;;) { - c = getc(); - if ('0' <= c && c <= '7') - value = value * 8 + (long)(c - '0'); - else { - token.longValue = value; - if (c == 'L' || c == 'l') - return LongConstant; - else { - ungetc(c); - return IntConstant; - } - } - } - } - - value = c - '0'; - while ('0' <= c2 && c2 <= '9') { - value = value * 10 + c2 - '0'; - c2 = getc(); - } - - token.longValue = value; - if (c2 == 'F' || c2 == 'f') { - token.doubleValue = (double)value; - return FloatConstant; - } - else if (c2 == 'E' || c2 == 'e' - || c2 == 'D' || c2 == 'd' || c2 == '.') { - StringBuffer tbuf = textBuffer; - tbuf.setLength(0); - tbuf.append(value); - return readDouble(tbuf, c2, token); - } - else if (c2 == 'L' || c2 == 'l') - return LongConstant; - else { - ungetc(c2); - return IntConstant; - } - } - - private int readDouble(StringBuffer sbuf, int c, Token token) { - if (c != 'E' && c != 'e' && c != 'D' && c != 'd') { - sbuf.append((char)c); - for (;;) { - c = getc(); - if ('0' <= c && c <= '9') - sbuf.append((char)c); - else - break; - } - } - - if (c == 'E' || c == 'e') { - sbuf.append((char)c); - c = getc(); - if (c == '+' || c == '-') { - sbuf.append((char)c); - c = getc(); - } - - while ('0' <= c && c <= '9') { - sbuf.append((char)c); - c = getc(); - } - } - - try { - token.doubleValue = Double.parseDouble(sbuf.toString()); - } - catch (NumberFormatException e) { - return BadToken; - } - - if (c == 'F' || c == 'f') - return FloatConstant; - else { - if (c != 'D' && c != 'd') - ungetc(c); - - return DoubleConstant; - } - } - - // !"#$%&'( )*+,-./0 12345678 9:;<=>? - private static final int[] equalOps - = { NEQ, 0, 0, 0, MOD_E, AND_E, 0, 0, - 0, MUL_E, PLUS_E, 0, MINUS_E, 0, DIV_E, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, LE, EQ, GE, 0 }; - - private int readSeparator(int c) { - int c2, c3; - if ('!' <= c && c <= '?') { - int t = equalOps[c - '!']; - if (t == 0) - return c; - else { - c2 = getc(); - if (c == c2) - switch (c) { - case '=' : - return EQ; - case '+' : - return PLUSPLUS; - case '-' : - return MINUSMINUS; - case '&' : - return ANDAND; - case '<' : - c3 = getc(); - if (c3 == '=') - return LSHIFT_E; - else { - ungetc(c3); - return LSHIFT; - } - case '>' : - c3 = getc(); - if (c3 == '=') - return RSHIFT_E; - else if (c3 == '>') { - c3 = getc(); - if (c3 == '=') - return ARSHIFT_E; - else { - ungetc(c3); - return ARSHIFT; - } - } - else { - ungetc(c3); - return RSHIFT; - } - default : - break; - } - else if (c2 == '=') - return t; - } - } - else if (c == '^') { - c2 = getc(); - if (c2 == '=') - return EXOR_E; - } - else if (c == '|') { - c2 = getc(); - if (c2 == '=') - return OR_E; - else if (c2 == '|') - return OROR; - } - else - return c; - - ungetc(c2); - return c; - } - - private int readIdentifier(int c, Token token) { - StringBuffer tbuf = textBuffer; - tbuf.setLength(0); - - do { - tbuf.append((char)c); - c = getc(); - } while (Character.isJavaIdentifierPart((char)c)); - - ungetc(c); - - String name = tbuf.toString(); - int t = ktable.lookup(name); - if (t >= 0) - return t; - else { - /* tbuf.toString() is executed quickly since it does not - * need memory copy. Using a hand-written extensible - * byte-array class instead of StringBuffer is not a good idea - * for execution speed. Converting a byte array to a String - * object is very slow. Using an extensible char array - * might be OK. - */ - token.textValue = name; - return Identifier; - } - } - - private static final KeywordTable ktable = new KeywordTable(); - - static { - ktable.append("abstract", ABSTRACT); - ktable.append("boolean", BOOLEAN); - ktable.append("break", BREAK); - ktable.append("byte", BYTE); - ktable.append("case", CASE); - ktable.append("catch", CATCH); - ktable.append("char", CHAR); - ktable.append("class", CLASS); - ktable.append("const", CONST); - ktable.append("continue", CONTINUE); - ktable.append("default", DEFAULT); - ktable.append("do", DO); - ktable.append("double", DOUBLE); - ktable.append("else", ELSE); - ktable.append("extends", EXTENDS); - ktable.append("false", FALSE); - ktable.append("final", FINAL); - ktable.append("finally", FINALLY); - ktable.append("float", FLOAT); - ktable.append("for", FOR); - ktable.append("goto", GOTO); - ktable.append("if", IF); - ktable.append("implements", IMPLEMENTS); - ktable.append("import", IMPORT); - ktable.append("instanceof", INSTANCEOF); - ktable.append("int", INT); - ktable.append("interface", INTERFACE); - ktable.append("long", LONG); - ktable.append("native", NATIVE); - ktable.append("new", NEW); - ktable.append("null", NULL); - ktable.append("package", PACKAGE); - ktable.append("private", PRIVATE); - ktable.append("protected", PROTECTED); - ktable.append("public", PUBLIC); - ktable.append("return", RETURN); - ktable.append("short", SHORT); - ktable.append("static", STATIC); - ktable.append("strictfp", STRICT); - ktable.append("super", SUPER); - ktable.append("switch", SWITCH); - ktable.append("synchronized", SYNCHRONIZED); - ktable.append("this", THIS); - ktable.append("throw", THROW); - ktable.append("throws", THROWS); - ktable.append("transient", TRANSIENT); - ktable.append("true", TRUE); - ktable.append("try", TRY); - ktable.append("void", VOID); - ktable.append("volatile", VOLATILE); - ktable.append("while", WHILE); - } - - private static boolean isBlank(int c) { - return c == ' ' || c == '\t' || c == '\f' || c == '\r' - || c == '\n'; - } - - private static boolean isDigit(int c) { - return '0' <= c && c <= '9'; - } - - private void ungetc(int c) { - lastChar = c; - } - - public String getTextAround() { - int begin = position - 10; - if (begin < 0) - begin = 0; - - int end = position + 10; - if (end > maxlen) - end = maxlen; - - return input.substring(begin, end); - } - - private int getc() { - if (lastChar < 0) - if (position < maxlen) - return input.charAt(position++); - else - return -1; - else { - int c = lastChar; - lastChar = -1; - return c; - } - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java b/src/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java deleted file mode 100644 index 91d3d28..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java +++ /dev/null @@ -1,1160 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.ast.*; - -import java.util.ArrayList; - -/* Code generator methods depending on javassist.* classes. - */ -public class MemberCodeGen extends CodeGen { - protected MemberResolver resolver; - protected CtClass thisClass; - protected MethodInfo thisMethod; - - protected boolean resultStatic; - - public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) { - super(b); - resolver = new MemberResolver(cp); - thisClass = cc; - thisMethod = null; - } - - /** - * Returns the major version of the class file - * targeted by this compilation. - */ - public int getMajorVersion() { - ClassFile cf = thisClass.getClassFile2(); - if (cf == null) - return ClassFile.MAJOR_VERSION; // JDK 1.3 - else - return cf.getMajorVersion(); - } - - /** - * Records the currently compiled method. - */ - public void setThisMethod(CtMethod m) { - thisMethod = m.getMethodInfo2(); - if (typeChecker != null) - typeChecker.setThisMethod(thisMethod); - } - - public CtClass getThisClass() { return thisClass; } - - /** - * Returns the JVM-internal representation of this class name. - */ - protected String getThisName() { - return MemberResolver.javaToJvmName(thisClass.getName()); - } - - /** - * Returns the JVM-internal representation of this super class name. - */ - protected String getSuperName() throws CompileError { - return MemberResolver.javaToJvmName( - MemberResolver.getSuperclass(thisClass).getName()); - } - - protected void insertDefaultSuperCall() throws CompileError { - bytecode.addAload(0); - bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass), - "", "()V"); - } - - static class JsrHook extends ReturnHook { - ArrayList jsrList; - CodeGen cgen; - int var; - - JsrHook(CodeGen gen) { - super(gen); - jsrList = new ArrayList(); - cgen = gen; - var = -1; - } - - private int getVar(int size) { - if (var < 0) { - var = cgen.getMaxLocals(); - cgen.incMaxLocals(size); - } - - return var; - } - - private void jsrJmp(Bytecode b) { - b.addOpcode(Opcode.GOTO); - jsrList.add(new int[] {b.currentPc(), var}); - b.addIndex(0); - } - - protected boolean doit(Bytecode b, int opcode) { - switch (opcode) { - case Opcode.RETURN : - jsrJmp(b); - break; - case ARETURN : - b.addAstore(getVar(1)); - jsrJmp(b); - b.addAload(var); - break; - case IRETURN : - b.addIstore(getVar(1)); - jsrJmp(b); - b.addIload(var); - break; - case LRETURN : - b.addLstore(getVar(2)); - jsrJmp(b); - b.addLload(var); - break; - case DRETURN : - b.addDstore(getVar(2)); - jsrJmp(b); - b.addDload(var); - break; - case FRETURN : - b.addFstore(getVar(1)); - jsrJmp(b); - b.addFload(var); - break; - default : - throw new RuntimeException("fatal"); - } - - return false; - } - } - - static class JsrHook2 extends ReturnHook { - int var; - int target; - - JsrHook2(CodeGen gen, int[] retTarget) { - super(gen); - target = retTarget[0]; - var = retTarget[1]; - } - - protected boolean doit(Bytecode b, int opcode) { - switch (opcode) { - case Opcode.RETURN : - break; - case ARETURN : - b.addAstore(var); - break; - case IRETURN : - b.addIstore(var); - break; - case LRETURN : - b.addLstore(var); - break; - case DRETURN : - b.addDstore(var); - break; - case FRETURN : - b.addFstore(var); - break; - default : - throw new RuntimeException("fatal"); - } - - b.addOpcode(Opcode.GOTO); - b.addIndex(target - b.currentPc() + 3); - return true; - } - } - - protected void atTryStmnt(Stmnt st) throws CompileError { - Bytecode bc = bytecode; - Stmnt body = (Stmnt)st.getLeft(); - if (body == null) - return; - - ASTList catchList = (ASTList)st.getRight().getLeft(); - Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft(); - ArrayList gotoList = new ArrayList(); - - JsrHook jsrHook = null; - if (finallyBlock != null) - jsrHook = new JsrHook(this); - - int start = bc.currentPc(); - body.accept(this); - int end = bc.currentPc(); - if (start == end) - throw new CompileError("empty try block"); - - boolean tryNotReturn = !hasReturned; - if (tryNotReturn) { - bc.addOpcode(Opcode.GOTO); - gotoList.add(new Integer(bc.currentPc())); - bc.addIndex(0); // correct later - } - - int var = getMaxLocals(); - incMaxLocals(1); - while (catchList != null) { - // catch clause - Pair p = (Pair)catchList.head(); - catchList = catchList.tail(); - Declarator decl = (Declarator)p.getLeft(); - Stmnt block = (Stmnt)p.getRight(); - - decl.setLocalVar(var); - - CtClass type = resolver.lookupClassByJvmName(decl.getClassName()); - decl.setClassName(MemberResolver.javaToJvmName(type.getName())); - bc.addExceptionHandler(start, end, bc.currentPc(), type); - bc.growStack(1); - bc.addAstore(var); - hasReturned = false; - if (block != null) - block.accept(this); - - if (!hasReturned) { - bc.addOpcode(Opcode.GOTO); - gotoList.add(new Integer(bc.currentPc())); - bc.addIndex(0); // correct later - tryNotReturn = true; - } - } - - if (finallyBlock != null) { - jsrHook.remove(this); - // catch (any) clause - int pcAnyCatch = bc.currentPc(); - bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0); - bc.growStack(1); - bc.addAstore(var); - hasReturned = false; - finallyBlock.accept(this); - if (!hasReturned) { - bc.addAload(var); - bc.addOpcode(ATHROW); - } - - addFinally(jsrHook.jsrList, finallyBlock); - } - - int pcEnd = bc.currentPc(); - patchGoto(gotoList, pcEnd); - hasReturned = !tryNotReturn; - if (finallyBlock != null) { - if (tryNotReturn) - finallyBlock.accept(this); - } - } - - /** - * Adds a finally clause for earch return statement. - */ - private void addFinally(ArrayList returnList, Stmnt finallyBlock) - throws CompileError - { - Bytecode bc = bytecode; - int n = returnList.size(); - for (int i = 0; i < n; ++i) { - final int[] ret = (int[])returnList.get(i); - int pc = ret[0]; - bc.write16bit(pc, bc.currentPc() - pc + 1); - ReturnHook hook = new JsrHook2(this, ret); - finallyBlock.accept(this); - hook.remove(this); - if (!hasReturned) { - bc.addOpcode(Opcode.GOTO); - bc.addIndex(pc + 3 - bc.currentPc()); - } - } - } - - public void atNewExpr(NewExpr expr) throws CompileError { - if (expr.isArray()) - atNewArrayExpr(expr); - else { - CtClass clazz = resolver.lookupClassByName(expr.getClassName()); - String cname = clazz.getName(); - ASTList args = expr.getArguments(); - bytecode.addNew(cname); - bytecode.addOpcode(DUP); - - atMethodCallCore(clazz, MethodInfo.nameInit, args, - false, true, -1, null); - - exprType = CLASS; - arrayDim = 0; - className = MemberResolver.javaToJvmName(cname); - } - } - - public void atNewArrayExpr(NewExpr expr) throws CompileError { - int type = expr.getArrayType(); - ASTList size = expr.getArraySize(); - ASTList classname = expr.getClassName(); - ArrayInit init = expr.getInitializer(); - if (size.length() > 1) { - if (init != null) - throw new CompileError( - "sorry, multi-dimensional array initializer " + - "for new is not supported"); - - atMultiNewArray(type, classname, size); - return; - } - - ASTree sizeExpr = size.head(); - atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init); - } - - private void atNewArrayExpr2(int type, ASTree sizeExpr, - String jvmClassname, ArrayInit init) throws CompileError { - if (init == null) - if (sizeExpr == null) - throw new CompileError("no array size"); - else - sizeExpr.accept(this); - else - if (sizeExpr == null) { - int s = init.length(); - bytecode.addIconst(s); - } - else - throw new CompileError("unnecessary array size specified for new"); - - String elementClass; - if (type == CLASS) { - elementClass = resolveClassName(jvmClassname); - bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass)); - } - else { - elementClass = null; - int atype = 0; - switch (type) { - case BOOLEAN : - atype = T_BOOLEAN; - break; - case CHAR : - atype = T_CHAR; - break; - case FLOAT : - atype = T_FLOAT; - break; - case DOUBLE : - atype = T_DOUBLE; - break; - case BYTE : - atype = T_BYTE; - break; - case SHORT : - atype = T_SHORT; - break; - case INT : - atype = T_INT; - break; - case LONG : - atype = T_LONG; - break; - default : - badNewExpr(); - break; - } - - bytecode.addOpcode(NEWARRAY); - bytecode.add(atype); - } - - if (init != null) { - int s = init.length(); - ASTList list = init; - for (int i = 0; i < s; i++) { - bytecode.addOpcode(DUP); - bytecode.addIconst(i); - list.head().accept(this); - if (!isRefType(type)) - atNumCastExpr(exprType, type); - - bytecode.addOpcode(getArrayWriteOp(type, 0)); - list = list.tail(); - } - } - - exprType = type; - arrayDim = 1; - className = elementClass; - } - - private static void badNewExpr() throws CompileError { - throw new CompileError("bad new expression"); - } - - protected void atArrayVariableAssign(ArrayInit init, int varType, - int varArray, String varClass) throws CompileError { - atNewArrayExpr2(varType, null, varClass, init); - } - - public void atArrayInit(ArrayInit init) throws CompileError { - throw new CompileError("array initializer is not supported"); - } - - protected void atMultiNewArray(int type, ASTList classname, ASTList size) - throws CompileError - { - int count, dim; - dim = size.length(); - for (count = 0; size != null; size = size.tail()) { - ASTree s = size.head(); - if (s == null) - break; // int[][][] a = new int[3][4][]; - - ++count; - s.accept(this); - if (exprType != INT) - throw new CompileError("bad type for array size"); - } - - String desc; - exprType = type; - arrayDim = dim; - if (type == CLASS) { - className = resolveClassName(classname); - desc = toJvmArrayName(className, dim); - } - else - desc = toJvmTypeName(type, dim); - - bytecode.addMultiNewarray(desc, count); - } - - public void atCallExpr(CallExpr expr) throws CompileError { - String mname = null; - CtClass targetClass = null; - ASTree method = expr.oprand1(); - ASTList args = (ASTList)expr.oprand2(); - boolean isStatic = false; - boolean isSpecial = false; - int aload0pos = -1; - - MemberResolver.Method cached = expr.getMethod(); - if (method instanceof Member) { - mname = ((Member)method).get(); - targetClass = thisClass; - if (inStaticMethod || (cached != null && cached.isStatic())) - isStatic = true; // should be static - else { - aload0pos = bytecode.currentPc(); - bytecode.addAload(0); // this - } - } - else if (method instanceof Keyword) { // constructor - isSpecial = true; - mname = MethodInfo.nameInit; // - targetClass = thisClass; - if (inStaticMethod) - throw new CompileError("a constructor cannot be static"); - else - bytecode.addAload(0); // this - - if (((Keyword)method).get() == SUPER) - targetClass = MemberResolver.getSuperclass(targetClass); - } - else if (method instanceof Expr) { - Expr e = (Expr)method; - mname = ((Symbol)e.oprand2()).get(); - int op = e.getOperator(); - if (op == MEMBER) { // static method - targetClass - = resolver.lookupClass(((Symbol)e.oprand1()).get(), false); - isStatic = true; - } - else if (op == '.') { - ASTree target = e.oprand1(); - String classFollowedByDotSuper = TypeChecker.isDotSuper(target); - if (classFollowedByDotSuper != null) { - isSpecial = true; - targetClass = MemberResolver.getSuperInterface(thisClass, - classFollowedByDotSuper); - if (inStaticMethod || (cached != null && cached.isStatic())) - isStatic = true; // should be static - else { - aload0pos = bytecode.currentPc(); - bytecode.addAload(0); // this - } - } - else { - if (target instanceof Keyword) - if (((Keyword)target).get() == SUPER) - isSpecial = true; - - try { - target.accept(this); - } - catch (NoFieldException nfe) { - if (nfe.getExpr() != target) - throw nfe; - - // it should be a static method. - exprType = CLASS; - arrayDim = 0; - className = nfe.getField(); // JVM-internal - isStatic = true; - } - - if (arrayDim > 0) - targetClass = resolver.lookupClass(javaLangObject, true); - else if (exprType == CLASS /* && arrayDim == 0 */) - targetClass = resolver.lookupClassByJvmName(className); - else - badMethod(); - } - } - else - badMethod(); - } - else - fatal(); - - atMethodCallCore(targetClass, mname, args, isStatic, isSpecial, - aload0pos, cached); - } - - private static void badMethod() throws CompileError { - throw new CompileError("bad method"); - } - - /* - * atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew - * - * @param targetClass the class at which method lookup starts. - * @param found not null if the method look has been already done. - */ - public void atMethodCallCore(CtClass targetClass, String mname, - ASTList args, boolean isStatic, boolean isSpecial, - int aload0pos, MemberResolver.Method found) - throws CompileError - { - int nargs = getMethodArgsLength(args); - int[] types = new int[nargs]; - int[] dims = new int[nargs]; - String[] cnames = new String[nargs]; - - if (!isStatic && found != null && found.isStatic()) { - bytecode.addOpcode(POP); - isStatic = true; - } - - int stack = bytecode.getStackDepth(); - - // generate code for evaluating arguments. - atMethodArgs(args, types, dims, cnames); - - if (found == null) - found = resolver.lookupMethod(targetClass, thisClass, thisMethod, - mname, types, dims, cnames); - - if (found == null) { - String msg; - if (mname.equals(MethodInfo.nameInit)) - msg = "constructor not found"; - else - msg = "Method " + mname + " not found in " - + targetClass.getName(); - - throw new CompileError(msg); - } - - atMethodCallCore2(targetClass, mname, isStatic, isSpecial, - aload0pos, found); - } - - private void atMethodCallCore2(CtClass targetClass, String mname, - boolean isStatic, boolean isSpecial, - int aload0pos, - MemberResolver.Method found) - throws CompileError - { - CtClass declClass = found.declaring; - MethodInfo minfo = found.info; - String desc = minfo.getDescriptor(); - int acc = minfo.getAccessFlags(); - - if (mname.equals(MethodInfo.nameInit)) { - isSpecial = true; - if (declClass != targetClass) - throw new CompileError("no such constructor: " + targetClass.getName()); - - if (declClass != thisClass && AccessFlag.isPrivate(acc)) { - desc = getAccessibleConstructor(desc, declClass, minfo); - bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter - } - } - else if (AccessFlag.isPrivate(acc)) - if (declClass == thisClass) - isSpecial = true; - else { - isSpecial = false; - isStatic = true; - String origDesc = desc; - if ((acc & AccessFlag.STATIC) == 0) - desc = Descriptor.insertParameter(declClass.getName(), - origDesc); - - acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC; - mname = getAccessiblePrivate(mname, origDesc, desc, - minfo, declClass); - } - - boolean popTarget = false; - if ((acc & AccessFlag.STATIC) != 0) { - if (!isStatic) { - /* this method is static but the target object is - on stack. It must be popped out. If aload0pos >= 0, - then the target object was pushed by aload_0. It is - overwritten by NOP. - */ - isStatic = true; - if (aload0pos >= 0) - bytecode.write(aload0pos, NOP); - else - popTarget = true; - } - - bytecode.addInvokestatic(declClass, mname, desc); - } - else if (isSpecial) // if (isSpecial && notStatic(acc)) - bytecode.addInvokespecial(declClass, mname, desc); - else { - if (!Modifier.isPublic(declClass.getModifiers()) - || declClass.isInterface() != targetClass.isInterface()) - declClass = targetClass; - - if (declClass.isInterface()) { - int nargs = Descriptor.paramSize(desc) + 1; - bytecode.addInvokeinterface(declClass, mname, desc, nargs); - } - else - if (isStatic) - throw new CompileError(mname + " is not static"); - else - bytecode.addInvokevirtual(declClass, mname, desc); - } - - setReturnType(desc, isStatic, popTarget); - } - - /* - * Finds (or adds if necessary) a hidden accessor if the method - * is in an enclosing class. - * - * @param desc the descriptor of the method. - * @param declClass the class declaring the method. - */ - protected String getAccessiblePrivate(String methodName, String desc, - String newDesc, MethodInfo minfo, - CtClass declClass) - throws CompileError - { - if (isEnclosing(declClass, thisClass)) { - AccessorMaker maker = declClass.getAccessorMaker(); - if (maker != null) - return maker.getMethodAccessor(methodName, desc, newDesc, - minfo); - } - - throw new CompileError("Method " + methodName - + " is private"); - } - - /* - * Finds (or adds if necessary) a hidden constructor if the given - * constructor is in an enclosing class. - * - * @param desc the descriptor of the constructor. - * @param declClass the class declaring the constructor. - * @param minfo the method info of the constructor. - * @return the descriptor of the hidden constructor. - */ - protected String getAccessibleConstructor(String desc, CtClass declClass, - MethodInfo minfo) - throws CompileError - { - if (isEnclosing(declClass, thisClass)) { - AccessorMaker maker = declClass.getAccessorMaker(); - if (maker != null) - return maker.getConstructor(declClass, desc, minfo); - } - - throw new CompileError("the called constructor is private in " - + declClass.getName()); - } - - private boolean isEnclosing(CtClass outer, CtClass inner) { - try { - while (inner != null) { - inner = inner.getDeclaringClass(); - if (inner == outer) - return true; - } - } - catch (NotFoundException e) {} - return false; - } - - public int getMethodArgsLength(ASTList args) { - return ASTList.length(args); - } - - public void atMethodArgs(ASTList args, int[] types, int[] dims, - String[] cnames) throws CompileError { - int i = 0; - while (args != null) { - ASTree a = args.head(); - a.accept(this); - types[i] = exprType; - dims[i] = arrayDim; - cnames[i] = className; - ++i; - args = args.tail(); - } - } - - void setReturnType(String desc, boolean isStatic, boolean popTarget) - throws CompileError - { - int i = desc.indexOf(')'); - if (i < 0) - badMethod(); - - char c = desc.charAt(++i); - int dim = 0; - while (c == '[') { - ++dim; - c = desc.charAt(++i); - } - - arrayDim = dim; - if (c == 'L') { - int j = desc.indexOf(';', i + 1); - if (j < 0) - badMethod(); - - exprType = CLASS; - className = desc.substring(i + 1, j); - } - else { - exprType = MemberResolver.descToType(c); - className = null; - } - - int etype = exprType; - if (isStatic) { - if (popTarget) { - if (is2word(etype, dim)) { - bytecode.addOpcode(DUP2_X1); - bytecode.addOpcode(POP2); - bytecode.addOpcode(POP); - } - else if (etype == VOID) - bytecode.addOpcode(POP); - else { - bytecode.addOpcode(SWAP); - bytecode.addOpcode(POP); - } - } - } - } - - protected void atFieldAssign(Expr expr, int op, ASTree left, - ASTree right, boolean doDup) throws CompileError - { - CtField f = fieldAccess(left, false); - boolean is_static = resultStatic; - if (op != '=' && !is_static) - bytecode.addOpcode(DUP); - - int fi; - if (op == '=') { - FieldInfo finfo = f.getFieldInfo2(); - setFieldType(finfo); - AccessorMaker maker = isAccessibleField(f, finfo); - if (maker == null) - fi = addFieldrefInfo(f, finfo); - else - fi = 0; - } - else - fi = atFieldRead(f, is_static); - - int fType = exprType; - int fDim = arrayDim; - String cname = className; - - atAssignCore(expr, op, right, fType, fDim, cname); - - boolean is2w = is2word(fType, fDim); - if (doDup) { - int dup_code; - if (is_static) - dup_code = (is2w ? DUP2 : DUP); - else - dup_code = (is2w ? DUP2_X1 : DUP_X1); - - bytecode.addOpcode(dup_code); - } - - atFieldAssignCore(f, is_static, fi, is2w); - - exprType = fType; - arrayDim = fDim; - className = cname; - } - - /* If fi == 0, the field must be a private field in an enclosing class. - */ - private void atFieldAssignCore(CtField f, boolean is_static, int fi, - boolean is2byte) throws CompileError { - if (fi != 0) { - if (is_static) { - bytecode.add(PUTSTATIC); - bytecode.growStack(is2byte ? -2 : -1); - } - else { - bytecode.add(PUTFIELD); - bytecode.growStack(is2byte ? -3 : -2); - } - - bytecode.addIndex(fi); - } - else { - CtClass declClass = f.getDeclaringClass(); - AccessorMaker maker = declClass.getAccessorMaker(); - // make should be non null. - FieldInfo finfo = f.getFieldInfo2(); - MethodInfo minfo = maker.getFieldSetter(finfo, is_static); - bytecode.addInvokestatic(declClass, minfo.getName(), - minfo.getDescriptor()); - } - } - - /* overwritten in JvstCodeGen. - */ - public void atMember(Member mem) throws CompileError { - atFieldRead(mem); - } - - protected void atFieldRead(ASTree expr) throws CompileError - { - CtField f = fieldAccess(expr, true); - if (f == null) { - atArrayLength(expr); - return; - } - - boolean is_static = resultStatic; - ASTree cexpr = TypeChecker.getConstantFieldValue(f); - if (cexpr == null) - atFieldRead(f, is_static); - else { - cexpr.accept(this); - setFieldType(f.getFieldInfo2()); - } - } - - private void atArrayLength(ASTree expr) throws CompileError { - if (arrayDim == 0) - throw new CompileError(".length applied to a non array"); - - bytecode.addOpcode(ARRAYLENGTH); - exprType = INT; - arrayDim = 0; - } - - /** - * Generates bytecode for reading a field value. - * It returns a fieldref_info index or zero if the field is a private - * one declared in an enclosing class. - */ - private int atFieldRead(CtField f, boolean isStatic) throws CompileError { - FieldInfo finfo = f.getFieldInfo2(); - boolean is2byte = setFieldType(finfo); - AccessorMaker maker = isAccessibleField(f, finfo); - if (maker != null) { - MethodInfo minfo = maker.getFieldGetter(finfo, isStatic); - bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(), - minfo.getDescriptor()); - return 0; - } - else { - int fi = addFieldrefInfo(f, finfo); - if (isStatic) { - bytecode.add(GETSTATIC); - bytecode.growStack(is2byte ? 2 : 1); - } - else { - bytecode.add(GETFIELD); - bytecode.growStack(is2byte ? 1 : 0); - } - - bytecode.addIndex(fi); - return fi; - } - } - - /** - * Returns null if the field is accessible. Otherwise, it throws - * an exception or it returns AccessorMaker if the field is a private - * one declared in an enclosing class. - */ - private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo) - throws CompileError - { - if (AccessFlag.isPrivate(finfo.getAccessFlags()) - && f.getDeclaringClass() != thisClass) { - CtClass declClass = f.getDeclaringClass(); - if (isEnclosing(declClass, thisClass)) { - AccessorMaker maker = declClass.getAccessorMaker(); - if (maker != null) - return maker; - else - throw new CompileError("fatal error. bug?"); - } - else - throw new CompileError("Field " + f.getName() + " in " - + declClass.getName() + " is private."); - } - - return null; // accessible field - } - - /** - * Sets exprType, arrayDim, and className. - * - * @return true if the field type is long or double. - */ - private boolean setFieldType(FieldInfo finfo) throws CompileError { - String type = finfo.getDescriptor(); - - int i = 0; - int dim = 0; - char c = type.charAt(i); - while (c == '[') { - ++dim; - c = type.charAt(++i); - } - - arrayDim = dim; - exprType = MemberResolver.descToType(c); - - if (c == 'L') - className = type.substring(i + 1, type.indexOf(';', i + 1)); - else - className = null; - - boolean is2byte = dim == 0 && (c == 'J' || c == 'D'); - return is2byte; - } - - private int addFieldrefInfo(CtField f, FieldInfo finfo) { - ConstPool cp = bytecode.getConstPool(); - String cname = f.getDeclaringClass().getName(); - int ci = cp.addClassInfo(cname); - String name = finfo.getName(); - String type = finfo.getDescriptor(); - return cp.addFieldrefInfo(ci, name, type); - } - - protected void atClassObject2(String cname) throws CompileError { - if (getMajorVersion() < ClassFile.JAVA_5) - super.atClassObject2(cname); - else - bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname)); - } - - protected void atFieldPlusPlus(int token, boolean isPost, - ASTree oprand, Expr expr, boolean doDup) - throws CompileError - { - CtField f = fieldAccess(oprand, false); - boolean is_static = resultStatic; - if (!is_static) - bytecode.addOpcode(DUP); - - int fi = atFieldRead(f, is_static); - int t = exprType; - boolean is2w = is2word(t, arrayDim); - - int dup_code; - if (is_static) - dup_code = (is2w ? DUP2 : DUP); - else - dup_code = (is2w ? DUP2_X1 : DUP_X1); - - atPlusPlusCore(dup_code, doDup, token, isPost, expr); - atFieldAssignCore(f, is_static, fi, is2w); - } - - /* This method also returns a value in resultStatic. - * - * @param acceptLength true if array length is acceptable - */ - protected CtField fieldAccess(ASTree expr, boolean acceptLength) - throws CompileError - { - if (expr instanceof Member) { - String name = ((Member)expr).get(); - CtField f = null; - try { - f = thisClass.getField(name); - } - catch (NotFoundException e) { - // EXPR might be part of a static member access? - throw new NoFieldException(name, expr); - } - - boolean is_static = Modifier.isStatic(f.getModifiers()); - if (!is_static) - if (inStaticMethod) - throw new CompileError( - "not available in a static method: " + name); - else - bytecode.addAload(0); // this - - resultStatic = is_static; - return f; - } - else if (expr instanceof Expr) { - Expr e = (Expr)expr; - int op = e.getOperator(); - if (op == MEMBER) { - /* static member by # (extension by Javassist) - * For example, if int.class is parsed, the resulting tree - * is (# "java.lang.Integer" "TYPE"). - */ - CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(), - (Symbol)e.oprand2()); - resultStatic = true; - return f; - } - else if (op == '.') { - CtField f = null; - try { - e.oprand1().accept(this); - /* Don't call lookupFieldByJvmName2(). - * The left operand of . is not a class name but - * a normal expression. - */ - if (exprType == CLASS && arrayDim == 0) - f = resolver.lookupFieldByJvmName(className, - (Symbol)e.oprand2()); - else if (acceptLength && arrayDim > 0 - && ((Symbol)e.oprand2()).get().equals("length")) - return null; // expr is an array length. - else - badLvalue(); - - boolean is_static = Modifier.isStatic(f.getModifiers()); - if (is_static) - bytecode.addOpcode(POP); - - resultStatic = is_static; - return f; - } - catch (NoFieldException nfe) { - if (nfe.getExpr() != e.oprand1()) - throw nfe; - - /* EXPR should be a static field. - * If EXPR might be part of a qualified class name, - * lookupFieldByJvmName2() throws NoFieldException. - */ - Symbol fname = (Symbol)e.oprand2(); - String cname = nfe.getField(); - f = resolver.lookupFieldByJvmName2(cname, fname, expr); - resultStatic = true; - return f; - } - } - else - badLvalue(); - } - else - badLvalue(); - - resultStatic = false; - return null; // never reach - } - - private static void badLvalue() throws CompileError { - throw new CompileError("bad l-value"); - } - - public CtClass[] makeParamList(MethodDecl md) throws CompileError { - CtClass[] params; - ASTList plist = md.getParams(); - if (plist == null) - params = new CtClass[0]; - else { - int i = 0; - params = new CtClass[plist.length()]; - while (plist != null) { - params[i++] = resolver.lookupClass((Declarator)plist.head()); - plist = plist.tail(); - } - } - - return params; - } - - public CtClass[] makeThrowsList(MethodDecl md) throws CompileError { - CtClass[] clist; - ASTList list = md.getThrows(); - if (list == null) - return null; - else { - int i = 0; - clist = new CtClass[list.length()]; - while (list != null) { - clist[i++] = resolver.lookupClassByName((ASTList)list.head()); - list = list.tail(); - } - - return clist; - } - } - - /* Converts a class name into a JVM-internal representation. - * - * It may also expand a simple class name to java.lang.*. - * For example, this converts Object into java/lang/Object. - */ - protected String resolveClassName(ASTList name) throws CompileError { - return resolver.resolveClassName(name); - } - - /* Expands a simple class name to java.lang.*. - * For example, this converts Object into java/lang/Object. - */ - protected String resolveClassName(String jvmName) throws CompileError { - return resolver.resolveJvmClassName(jvmName); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/MemberResolver.java b/src/com/wenshuo/agent/javassist/compiler/MemberResolver.java deleted file mode 100644 index 933a626..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/MemberResolver.java +++ /dev/null @@ -1,617 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import java.util.Hashtable; -import java.lang.ref.WeakReference; -import java.util.WeakHashMap; -import java.util.List; -import java.util.Iterator; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.ast.*; - -/* Code generator methods depending on javassist.* classes. - */ -public class MemberResolver implements TokenId { - private ClassPool classPool; - - public MemberResolver(ClassPool cp) { - classPool = cp; - } - - public ClassPool getClassPool() { return classPool; } - - private static void fatal() throws CompileError { - throw new CompileError("fatal"); - } - - public static class Method { - public CtClass declaring; - public MethodInfo info; - public int notmatch; - - public Method(CtClass c, MethodInfo i, int n) { - declaring = c; - info = i; - notmatch = n; - } - - /** - * Returns true if the invoked method is static. - */ - public boolean isStatic() { - int acc = info.getAccessFlags(); - return (acc & AccessFlag.STATIC) != 0; - } - } - - public Method lookupMethod(CtClass clazz, CtClass currentClass, MethodInfo current, - String methodName, - int[] argTypes, int[] argDims, - String[] argClassNames) - throws CompileError - { - Method maybe = null; - // to enable the creation of a recursively called method - if (current != null && clazz == currentClass) - if (current.getName().equals(methodName)) { - int res = compareSignature(current.getDescriptor(), - argTypes, argDims, argClassNames); - if (res != NO) { - Method r = new Method(clazz, current, res); - if (res == YES) - return r; - else - maybe = r; - } - } - - Method m = lookupMethod(clazz, methodName, argTypes, argDims, - argClassNames, maybe != null); - if (m != null) - return m; - else - return maybe; - } - - private Method lookupMethod(CtClass clazz, String methodName, - int[] argTypes, int[] argDims, - String[] argClassNames, boolean onlyExact) - throws CompileError - { - Method maybe = null; - ClassFile cf = clazz.getClassFile2(); - // If the class is an array type, the class file is null. - // If so, search the super class java.lang.Object for clone() etc. - if (cf != null) { - List list = cf.getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - if (minfo.getName().equals(methodName)) { - int res = compareSignature(minfo.getDescriptor(), - argTypes, argDims, argClassNames); - if (res != NO) { - Method r = new Method(clazz, minfo, res); - if (res == YES) - return r; - else if (maybe == null || maybe.notmatch > res) - maybe = r; - } - } - } - } - - if (onlyExact) - maybe = null; - else - onlyExact = maybe != null; - - int mod = clazz.getModifiers(); - boolean isIntf = Modifier.isInterface(mod); - try { - // skip searching java.lang.Object if clazz is an interface type. - if (!isIntf) { - CtClass pclazz = clazz.getSuperclass(); - if (pclazz != null) { - Method r = lookupMethod(pclazz, methodName, argTypes, - argDims, argClassNames, onlyExact); - if (r != null) - return r; - } - } - } - catch (NotFoundException e) {} - - try { - CtClass[] ifs = clazz.getInterfaces(); - int size = ifs.length; - for (int i = 0; i < size; ++i) { - Method r = lookupMethod(ifs[i], methodName, - argTypes, argDims, argClassNames, - onlyExact); - if (r != null) - return r; - } - - if (isIntf) { - // finally search java.lang.Object. - CtClass pclazz = clazz.getSuperclass(); - if (pclazz != null) { - Method r = lookupMethod(pclazz, methodName, argTypes, - argDims, argClassNames, onlyExact); - if (r != null) - return r; - } - } - } - catch (NotFoundException e) {} - - return maybe; - } - - private static final int YES = 0; - private static final int NO = -1; - - /* - * Returns YES if actual parameter types matches the given signature. - * - * argTypes, argDims, and argClassNames represent actual parameters. - * - * This method does not correctly implement the Java method dispatch - * algorithm. - * - * If some of the parameter types exactly match but others are subtypes of - * the corresponding type in the signature, this method returns the number - * of parameter types that do not exactly match. - */ - private int compareSignature(String desc, int[] argTypes, - int[] argDims, String[] argClassNames) - throws CompileError - { - int result = YES; - int i = 1; - int nArgs = argTypes.length; - if (nArgs != Descriptor.numOfParameters(desc)) - return NO; - - int len = desc.length(); - for (int n = 0; i < len; ++n) { - char c = desc.charAt(i++); - if (c == ')') - return (n == nArgs ? result : NO); - else if (n >= nArgs) - return NO; - - int dim = 0; - while (c == '[') { - ++dim; - c = desc.charAt(i++); - } - - if (argTypes[n] == NULL) { - if (dim == 0 && c != 'L') - return NO; - - if (c == 'L') - i = desc.indexOf(';', i) + 1; - } - else if (argDims[n] != dim) { - if (!(dim == 0 && c == 'L' - && desc.startsWith("java/lang/Object;", i))) - return NO; - - // if the thread reaches here, c must be 'L'. - i = desc.indexOf(';', i) + 1; - result++; - if (i <= 0) - return NO; // invalid descriptor? - } - else if (c == 'L') { // not compare - int j = desc.indexOf(';', i); - if (j < 0 || argTypes[n] != CLASS) - return NO; - - String cname = desc.substring(i, j); - if (!cname.equals(argClassNames[n])) { - CtClass clazz = lookupClassByJvmName(argClassNames[n]); - try { - if (clazz.subtypeOf(lookupClassByJvmName(cname))) - result++; - else - return NO; - } - catch (NotFoundException e) { - result++; // should be NO? - } - } - - i = j + 1; - } - else { - int t = descToType(c); - int at = argTypes[n]; - if (t != at) - if (t == INT - && (at == SHORT || at == BYTE || at == CHAR)) - result++; - else - return NO; - } - } - - return NO; - } - - /** - * Only used by fieldAccess() in MemberCodeGen and TypeChecker. - * - * @param jvmClassName a JVM class name. e.g. java/lang/String - * @see #lookupClass(String, boolean) - */ - public CtField lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym, - ASTree expr) throws NoFieldException - { - String field = fieldSym.get(); - CtClass cc = null; - try { - cc = lookupClass(jvmToJavaName(jvmClassName), true); - } - catch (CompileError e) { - // EXPR might be part of a qualified class name. - throw new NoFieldException(jvmClassName + "/" + field, expr); - } - - try { - return cc.getField(field); - } - catch (NotFoundException e) { - // maybe an inner class. - jvmClassName = javaToJvmName(cc.getName()); - throw new NoFieldException(jvmClassName + "$" + field, expr); - } - } - - /** - * @param jvmClassName a JVM class name. e.g. java/lang/String - */ - public CtField lookupFieldByJvmName(String jvmClassName, Symbol fieldName) - throws CompileError - { - return lookupField(jvmToJavaName(jvmClassName), fieldName); - } - - /** - * @param className a qualified class name. e.g. java.lang.String - */ - public CtField lookupField(String className, Symbol fieldName) - throws CompileError - { - CtClass cc = lookupClass(className, false); - try { - return cc.getField(fieldName.get()); - } - catch (NotFoundException e) {} - throw new CompileError("no such field: " + fieldName.get()); - } - - public CtClass lookupClassByName(ASTList name) throws CompileError { - return lookupClass(Declarator.astToClassName(name, '.'), false); - } - - public CtClass lookupClassByJvmName(String jvmName) throws CompileError { - return lookupClass(jvmToJavaName(jvmName), false); - } - - public CtClass lookupClass(Declarator decl) throws CompileError { - return lookupClass(decl.getType(), decl.getArrayDim(), - decl.getClassName()); - } - - /** - * @param classname jvm class name. - */ - public CtClass lookupClass(int type, int dim, String classname) - throws CompileError - { - String cname = ""; - CtClass clazz; - if (type == CLASS) { - clazz = lookupClassByJvmName(classname); - if (dim > 0) - cname = clazz.getName(); - else - return clazz; - } - else - cname = getTypeName(type); - - while (dim-- > 0) - cname += "[]"; - - return lookupClass(cname, false); - } - - /* - * type cannot be CLASS - */ - static String getTypeName(int type) throws CompileError { - String cname = ""; - switch (type) { - case BOOLEAN : - cname = "boolean"; - break; - case CHAR : - cname = "char"; - break; - case BYTE : - cname = "byte"; - break; - case SHORT : - cname = "short"; - break; - case INT : - cname = "int"; - break; - case LONG : - cname = "long"; - break; - case FLOAT : - cname = "float"; - break; - case DOUBLE : - cname = "double"; - break; - case VOID : - cname = "void"; - break; - default : - fatal(); - } - - return cname; - } - - /** - * @param name a qualified class name. e.g. java.lang.String - */ - public CtClass lookupClass(String name, boolean notCheckInner) - throws CompileError - { - Hashtable cache = getInvalidNames(); - Object found = cache.get(name); - if (found == INVALID) - throw new CompileError("no such class: " + name); - else if (found != null) - try { - return classPool.get((String)found); - } - catch (NotFoundException e) {} - - CtClass cc = null; - try { - cc = lookupClass0(name, notCheckInner); - } - catch (NotFoundException e) { - cc = searchImports(name); - } - - cache.put(name, cc.getName()); - return cc; - } - - private static final String INVALID = ""; - private static WeakHashMap invalidNamesMap = new WeakHashMap(); - private Hashtable invalidNames = null; - - // for unit tests - public static int getInvalidMapSize() { return invalidNamesMap.size(); } - - private Hashtable getInvalidNames() { - Hashtable ht = invalidNames; - if (ht == null) { - synchronized (MemberResolver.class) { - WeakReference ref = (WeakReference)invalidNamesMap.get(classPool); - if (ref != null) - ht = (Hashtable)ref.get(); - - if (ht == null) { - ht = new Hashtable(); - invalidNamesMap.put(classPool, new WeakReference(ht)); - } - } - - invalidNames = ht; - } - - return ht; - } - - private CtClass searchImports(String orgName) - throws CompileError - { - if (orgName.indexOf('.') < 0) { - Iterator it = classPool.getImportedPackages(); - while (it.hasNext()) { - String pac = (String)it.next(); - String fqName = pac + '.' + orgName; - try { - return classPool.get(fqName); - } - catch (NotFoundException e) { - try { - if (pac.endsWith("." + orgName)) - return classPool.get(pac); - } - catch (NotFoundException e2) {} - } - } - } - - getInvalidNames().put(orgName, INVALID); - throw new CompileError("no such class: " + orgName); - } - - private CtClass lookupClass0(String classname, boolean notCheckInner) - throws NotFoundException - { - CtClass cc = null; - do { - try { - cc = classPool.get(classname); - } - catch (NotFoundException e) { - int i = classname.lastIndexOf('.'); - if (notCheckInner || i < 0) - throw e; - else { - StringBuffer sbuf = new StringBuffer(classname); - sbuf.setCharAt(i, '$'); - classname = sbuf.toString(); - } - } - } while (cc == null); - return cc; - } - - /* Converts a class name into a JVM-internal representation. - * - * It may also expand a simple class name to java.lang.*. - * For example, this converts Object into java/lang/Object. - */ - public String resolveClassName(ASTList name) throws CompileError { - if (name == null) - return null; - else - return javaToJvmName(lookupClassByName(name).getName()); - } - - /* Expands a simple class name to java.lang.*. - * For example, this converts Object into java/lang/Object. - */ - public String resolveJvmClassName(String jvmName) throws CompileError { - if (jvmName == null) - return null; - else - return javaToJvmName(lookupClassByJvmName(jvmName).getName()); - } - - public static CtClass getSuperclass(CtClass c) throws CompileError { - try { - CtClass sc = c.getSuperclass(); - if (sc != null) - return sc; - } - catch (NotFoundException e) {} - throw new CompileError("cannot find the super class of " - + c.getName()); - } - - public static CtClass getSuperInterface(CtClass c, String interfaceName) - throws CompileError - { - try { - CtClass[] intfs = c.getInterfaces(); - for (int i = 0; i < intfs.length; i++) - if (intfs[i].getName().equals(interfaceName)) - return intfs[i]; - } catch (NotFoundException e) {} - throw new CompileError("cannot find the super inetrface " + interfaceName - + " of " + c.getName()); - } - - public static String javaToJvmName(String classname) { - return classname.replace('.', '/'); - } - - public static String jvmToJavaName(String classname) { - return classname.replace('/', '.'); - } - - public static int descToType(char c) throws CompileError { - switch (c) { - case 'Z' : - return BOOLEAN; - case 'C' : - return CHAR; - case 'B' : - return BYTE; - case 'S' : - return SHORT; - case 'I' : - return INT; - case 'J' : - return LONG; - case 'F' : - return FLOAT; - case 'D' : - return DOUBLE; - case 'V' : - return VOID; - case 'L' : - case '[' : - return CLASS; - default : - fatal(); - return VOID; // never reach here - } - } - - public static int getModifiers(ASTList mods) { - int m = 0; - while (mods != null) { - Keyword k = (Keyword)mods.head(); - mods = mods.tail(); - switch (k.get()) { - case STATIC : - m |= Modifier.STATIC; - break; - case FINAL : - m |= Modifier.FINAL; - break; - case SYNCHRONIZED : - m |= Modifier.SYNCHRONIZED; - break; - case ABSTRACT : - m |= Modifier.ABSTRACT; - break; - case PUBLIC : - m |= Modifier.PUBLIC; - break; - case PROTECTED : - m |= Modifier.PROTECTED; - break; - case PRIVATE : - m |= Modifier.PRIVATE; - break; - case VOLATILE : - m |= Modifier.VOLATILE; - break; - case TRANSIENT : - m |= Modifier.TRANSIENT; - break; - case STRICT : - m |= Modifier.STRICT; - break; - } - } - - return m; - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/NoFieldException.java b/src/com/wenshuo/agent/javassist/compiler/NoFieldException.java deleted file mode 100644 index 5b35107..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/NoFieldException.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.compiler.ast.ASTree; - -public class NoFieldException extends CompileError { - private String fieldName; - private ASTree expr; - - /* NAME must be JVM-internal representation. - */ - public NoFieldException(String name, ASTree e) { - super("no such field: " + name); - fieldName = name; - expr = e; - } - - /* The returned name should be JVM-internal representation. - */ - public String getField() { return fieldName; } - - /* Returns the expression where this exception is thrown. - */ - public ASTree getExpr() { return expr; } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/Parser.java b/src/com/wenshuo/agent/javassist/compiler/Parser.java deleted file mode 100644 index 4e15650..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/Parser.java +++ /dev/null @@ -1,1345 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.compiler.ast.*; - -public final class Parser implements TokenId { - private Lex lex; - - public Parser(Lex lex) { - this.lex = lex; - } - - public boolean hasMore() { return lex.lookAhead() >= 0; } - - /* member.declaration - * : method.declaration | field.declaration - */ - public ASTList parseMember(SymbolTable tbl) throws CompileError { - ASTList mem = parseMember1(tbl); - if (mem instanceof MethodDecl) - return parseMethod2(tbl, (MethodDecl)mem); - else - return mem; - } - - /* A method body is not parsed. - */ - public ASTList parseMember1(SymbolTable tbl) throws CompileError { - ASTList mods = parseMemberMods(); - Declarator d; - boolean isConstructor = false; - if (lex.lookAhead() == Identifier && lex.lookAhead(1) == '(') { - d = new Declarator(VOID, 0); - isConstructor = true; - } - else - d = parseFormalType(tbl); - - if (lex.get() != Identifier) - throw new SyntaxError(lex); - - String name; - if (isConstructor) - name = MethodDecl.initName; - else - name = lex.getString(); - - d.setVariable(new Symbol(name)); - if (isConstructor || lex.lookAhead() == '(') - return parseMethod1(tbl, isConstructor, mods, d); - else - return parseField(tbl, mods, d); - } - - /* field.declaration - * : member.modifiers - * formal.type Identifier - * [ "=" expression ] ";" - */ - private FieldDecl parseField(SymbolTable tbl, ASTList mods, - Declarator d) throws CompileError - { - ASTree expr = null; - if (lex.lookAhead() == '=') { - lex.get(); - expr = parseExpression(tbl); - } - - int c = lex.get(); - if (c == ';') - return new FieldDecl(mods, new ASTList(d, new ASTList(expr))); - else if (c == ',') - throw new CompileError( - "only one field can be declared in one declaration", lex); - else - throw new SyntaxError(lex); - } - - /* method.declaration - * : member.modifiers - * [ formal.type ] - * Identifier "(" [ formal.parameter ( "," formal.parameter )* ] ")" - * array.dimension - * [ THROWS class.type ( "," class.type ) ] - * ( block.statement | ";" ) - * - * Note that a method body is not parsed. - */ - private MethodDecl parseMethod1(SymbolTable tbl, boolean isConstructor, - ASTList mods, Declarator d) - throws CompileError - { - if (lex.get() != '(') - throw new SyntaxError(lex); - - ASTList parms = null; - if (lex.lookAhead() != ')') - while (true) { - parms = ASTList.append(parms, parseFormalParam(tbl)); - int t = lex.lookAhead(); - if (t == ',') - lex.get(); - else if (t == ')') - break; - } - - lex.get(); // ')' - d.addArrayDim(parseArrayDimension()); - if (isConstructor && d.getArrayDim() > 0) - throw new SyntaxError(lex); - - ASTList throwsList = null; - if (lex.lookAhead() == THROWS) { - lex.get(); - while (true) { - throwsList = ASTList.append(throwsList, parseClassType(tbl)); - if (lex.lookAhead() == ',') - lex.get(); - else - break; - } - } - - return new MethodDecl(mods, new ASTList(d, - ASTList.make(parms, throwsList, null))); - } - - /* Parses a method body. - */ - public MethodDecl parseMethod2(SymbolTable tbl, MethodDecl md) - throws CompileError - { - Stmnt body = null; - if (lex.lookAhead() == ';') - lex.get(); - else { - body = parseBlock(tbl); - if (body == null) - body = new Stmnt(BLOCK); - } - - md.sublist(4).setHead(body); - return md; - } - - /* member.modifiers - * : ( FINAL | SYNCHRONIZED | ABSTRACT - * | PUBLIC | PROTECTED | PRIVATE | STATIC - * | VOLATILE | TRANSIENT | STRICT )* - */ - private ASTList parseMemberMods() { - int t; - ASTList list = null; - while (true) { - t = lex.lookAhead(); - if (t == ABSTRACT || t == FINAL || t == PUBLIC || t == PROTECTED - || t == PRIVATE || t == SYNCHRONIZED || t == STATIC - || t == VOLATILE || t == TRANSIENT || t == STRICT) - list = new ASTList(new Keyword(lex.get()), list); - else - break; - } - - return list; - } - - /* formal.type : ( build-in-type | class.type ) array.dimension - */ - private Declarator parseFormalType(SymbolTable tbl) throws CompileError { - int t = lex.lookAhead(); - if (isBuiltinType(t) || t == VOID) { - lex.get(); // primitive type - int dim = parseArrayDimension(); - return new Declarator(t, dim); - } - else { - ASTList name = parseClassType(tbl); - int dim = parseArrayDimension(); - return new Declarator(name, dim); - } - } - - private static boolean isBuiltinType(int t) { - return (t == BOOLEAN || t == BYTE || t == CHAR || t == SHORT - || t == INT || t == LONG || t == FLOAT || t == DOUBLE); - } - - /* formal.parameter : formal.type Identifier array.dimension - */ - private Declarator parseFormalParam(SymbolTable tbl) - throws CompileError - { - Declarator d = parseFormalType(tbl); - if (lex.get() != Identifier) - throw new SyntaxError(lex); - - String name = lex.getString(); - d.setVariable(new Symbol(name)); - d.addArrayDim(parseArrayDimension()); - tbl.append(name, d); - return d; - } - - /* statement : [ label ":" ]* labeled.statement - * - * labeled.statement - * : block.statement - * | if.statement - * | while.statement - * | do.statement - * | for.statement - * | switch.statement - * | try.statement - * | return.statement - * | thorw.statement - * | break.statement - * | continue.statement - * | declaration.or.expression - * | ";" - * - * This method may return null (empty statement). - */ - public Stmnt parseStatement(SymbolTable tbl) - throws CompileError - { - int t = lex.lookAhead(); - if (t == '{') - return parseBlock(tbl); - else if (t == ';') { - lex.get(); - return new Stmnt(BLOCK); // empty statement - } - else if (t == Identifier && lex.lookAhead(1) == ':') { - lex.get(); // Identifier - String label = lex.getString(); - lex.get(); // ':' - return Stmnt.make(LABEL, new Symbol(label), parseStatement(tbl)); - } - else if (t == IF) - return parseIf(tbl); - else if (t == WHILE) - return parseWhile(tbl); - else if (t == DO) - return parseDo(tbl); - else if (t == FOR) - return parseFor(tbl); - else if (t == TRY) - return parseTry(tbl); - else if (t == SWITCH) - return parseSwitch(tbl); - else if (t == SYNCHRONIZED) - return parseSynchronized(tbl); - else if (t == RETURN) - return parseReturn(tbl); - else if (t == THROW) - return parseThrow(tbl); - else if (t == BREAK) - return parseBreak(tbl); - else if (t == CONTINUE) - return parseContinue(tbl); - else - return parseDeclarationOrExpression(tbl, false); - } - - /* block.statement : "{" statement* "}" - */ - private Stmnt parseBlock(SymbolTable tbl) throws CompileError { - if (lex.get() != '{') - throw new SyntaxError(lex); - - Stmnt body = null; - SymbolTable tbl2 = new SymbolTable(tbl); - while (lex.lookAhead() != '}') { - Stmnt s = parseStatement(tbl2); - if (s != null) - body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s)); - } - - lex.get(); // '}' - if (body == null) - return new Stmnt(BLOCK); // empty block - else - return body; - } - - /* if.statement : IF "(" expression ")" statement - * [ ELSE statement ] - */ - private Stmnt parseIf(SymbolTable tbl) throws CompileError { - int t = lex.get(); // IF - ASTree expr = parseParExpression(tbl); - Stmnt thenp = parseStatement(tbl); - Stmnt elsep; - if (lex.lookAhead() == ELSE) { - lex.get(); - elsep = parseStatement(tbl); - } - else - elsep = null; - - return new Stmnt(t, expr, new ASTList(thenp, new ASTList(elsep))); - } - - /* while.statement : WHILE "(" expression ")" statement - */ - private Stmnt parseWhile(SymbolTable tbl) - throws CompileError - { - int t = lex.get(); // WHILE - ASTree expr = parseParExpression(tbl); - Stmnt body = parseStatement(tbl); - return new Stmnt(t, expr, body); - } - - /* do.statement : DO statement WHILE "(" expression ")" ";" - */ - private Stmnt parseDo(SymbolTable tbl) throws CompileError { - int t = lex.get(); // DO - Stmnt body = parseStatement(tbl); - if (lex.get() != WHILE || lex.get() != '(') - throw new SyntaxError(lex); - - ASTree expr = parseExpression(tbl); - if (lex.get() != ')' || lex.get() != ';') - throw new SyntaxError(lex); - - return new Stmnt(t, expr, body); - } - - /* for.statement : FOR "(" decl.or.expr expression ";" expression ")" - * statement - */ - private Stmnt parseFor(SymbolTable tbl) throws CompileError { - Stmnt expr1, expr3; - ASTree expr2; - int t = lex.get(); // FOR - - SymbolTable tbl2 = new SymbolTable(tbl); - - if (lex.get() != '(') - throw new SyntaxError(lex); - - if (lex.lookAhead() == ';') { - lex.get(); - expr1 = null; - } - else - expr1 = parseDeclarationOrExpression(tbl2, true); - - if (lex.lookAhead() == ';') - expr2 = null; - else - expr2 = parseExpression(tbl2); - - if (lex.get() != ';') - throw new CompileError("; is missing", lex); - - if (lex.lookAhead() == ')') - expr3 = null; - else - expr3 = parseExprList(tbl2); - - if (lex.get() != ')') - throw new CompileError(") is missing", lex); - - Stmnt body = parseStatement(tbl2); - return new Stmnt(t, expr1, new ASTList(expr2, - new ASTList(expr3, body))); - } - - /* switch.statement : SWITCH "(" expression ")" "{" switch.block "}" - * - * swtich.block : ( switch.label statement* )* - * - * swtich.label : DEFAULT ":" - * | CASE const.expression ":" - */ - private Stmnt parseSwitch(SymbolTable tbl) throws CompileError { - int t = lex.get(); // SWITCH - ASTree expr = parseParExpression(tbl); - Stmnt body = parseSwitchBlock(tbl); - return new Stmnt(t, expr, body); - } - - private Stmnt parseSwitchBlock(SymbolTable tbl) throws CompileError { - if (lex.get() != '{') - throw new SyntaxError(lex); - - SymbolTable tbl2 = new SymbolTable(tbl); - Stmnt s = parseStmntOrCase(tbl2); - if (s == null) - throw new CompileError("empty switch block", lex); - - int op = s.getOperator(); - if (op != CASE && op != DEFAULT) - throw new CompileError("no case or default in a switch block", - lex); - - Stmnt body = new Stmnt(BLOCK, s); - while (lex.lookAhead() != '}') { - Stmnt s2 = parseStmntOrCase(tbl2); - if (s2 != null) { - int op2 = s2.getOperator(); - if (op2 == CASE || op2 == DEFAULT) { - body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s2)); - s = s2; - } - else - s = (Stmnt)ASTList.concat(s, new Stmnt(BLOCK, s2)); - } - } - - lex.get(); // '}' - return body; - } - - private Stmnt parseStmntOrCase(SymbolTable tbl) throws CompileError { - int t = lex.lookAhead(); - if (t != CASE && t != DEFAULT) - return parseStatement(tbl); - - lex.get(); - Stmnt s; - if (t == CASE) - s = new Stmnt(t, parseExpression(tbl)); - else - s = new Stmnt(DEFAULT); - - if (lex.get() != ':') - throw new CompileError(": is missing", lex); - - return s; - } - - /* synchronized.statement : - * SYNCHRONIZED "(" expression ")" block.statement - */ - private Stmnt parseSynchronized(SymbolTable tbl) throws CompileError { - int t = lex.get(); // SYNCHRONIZED - if (lex.get() != '(') - throw new SyntaxError(lex); - - ASTree expr = parseExpression(tbl); - if (lex.get() != ')') - throw new SyntaxError(lex); - - Stmnt body = parseBlock(tbl); - return new Stmnt(t, expr, body); - } - - /* try.statement - * : TRY block.statement - * [ CATCH "(" class.type Identifier ")" block.statement ]* - * [ FINALLY block.statement ]* - */ - private Stmnt parseTry(SymbolTable tbl) throws CompileError { - lex.get(); // TRY - Stmnt block = parseBlock(tbl); - ASTList catchList = null; - while (lex.lookAhead() == CATCH) { - lex.get(); // CATCH - if (lex.get() != '(') - throw new SyntaxError(lex); - - SymbolTable tbl2 = new SymbolTable(tbl); - Declarator d = parseFormalParam(tbl2); - if (d.getArrayDim() > 0 || d.getType() != CLASS) - throw new SyntaxError(lex); - - if (lex.get() != ')') - throw new SyntaxError(lex); - - Stmnt b = parseBlock(tbl2); - catchList = ASTList.append(catchList, new Pair(d, b)); - } - - Stmnt finallyBlock = null; - if (lex.lookAhead() == FINALLY) { - lex.get(); // FINALLY - finallyBlock = parseBlock(tbl); - } - - return Stmnt.make(TRY, block, catchList, finallyBlock); - } - - /* return.statement : RETURN [ expression ] ";" - */ - private Stmnt parseReturn(SymbolTable tbl) throws CompileError { - int t = lex.get(); // RETURN - Stmnt s = new Stmnt(t); - if (lex.lookAhead() != ';') - s.setLeft(parseExpression(tbl)); - - if (lex.get() != ';') - throw new CompileError("; is missing", lex); - - return s; - } - - /* throw.statement : THROW expression ";" - */ - private Stmnt parseThrow(SymbolTable tbl) throws CompileError { - int t = lex.get(); // THROW - ASTree expr = parseExpression(tbl); - if (lex.get() != ';') - throw new CompileError("; is missing", lex); - - return new Stmnt(t, expr); - } - - /* break.statement : BREAK [ Identifier ] ";" - */ - private Stmnt parseBreak(SymbolTable tbl) - throws CompileError - { - return parseContinue(tbl); - } - - /* continue.statement : CONTINUE [ Identifier ] ";" - */ - private Stmnt parseContinue(SymbolTable tbl) - throws CompileError - { - int t = lex.get(); // CONTINUE - Stmnt s = new Stmnt(t); - int t2 = lex.get(); - if (t2 == Identifier) { - s.setLeft(new Symbol(lex.getString())); - t2 = lex.get(); - } - - if (t2 != ';') - throw new CompileError("; is missing", lex); - - return s; - } - - /* declaration.or.expression - * : [ FINAL ] built-in-type array.dimension declarators - * | [ FINAL ] class.type array.dimension declarators - * | expression ';' - * | expr.list ';' if exprList is true - * - * Note: FINAL is currently ignored. This must be fixed - * in future. - */ - private Stmnt parseDeclarationOrExpression(SymbolTable tbl, - boolean exprList) - throws CompileError - { - int t = lex.lookAhead(); - while (t == FINAL) { - lex.get(); - t = lex.lookAhead(); - } - - if (isBuiltinType(t)) { - t = lex.get(); - int dim = parseArrayDimension(); - return parseDeclarators(tbl, new Declarator(t, dim)); - } - else if (t == Identifier) { - int i = nextIsClassType(0); - if (i >= 0) - if (lex.lookAhead(i) == Identifier) { - ASTList name = parseClassType(tbl); - int dim = parseArrayDimension(); - return parseDeclarators(tbl, new Declarator(name, dim)); - } - } - - Stmnt expr; - if (exprList) - expr = parseExprList(tbl); - else - expr = new Stmnt(EXPR, parseExpression(tbl)); - - if (lex.get() != ';') - throw new CompileError("; is missing", lex); - - return expr; - } - - /* expr.list : ( expression ',')* expression - */ - private Stmnt parseExprList(SymbolTable tbl) throws CompileError { - Stmnt expr = null; - for (;;) { - Stmnt e = new Stmnt(EXPR, parseExpression(tbl)); - expr = (Stmnt)ASTList.concat(expr, new Stmnt(BLOCK, e)); - if (lex.lookAhead() == ',') - lex.get(); - else - return expr; - } - } - - /* declarators : declarator [ ',' declarator ]* ';' - */ - private Stmnt parseDeclarators(SymbolTable tbl, Declarator d) - throws CompileError - { - Stmnt decl = null; - for (;;) { - decl = (Stmnt)ASTList.concat(decl, - new Stmnt(DECL, parseDeclarator(tbl, d))); - int t = lex.get(); - if (t == ';') - return decl; - else if (t != ',') - throw new CompileError("; is missing", lex); - } - } - - /* declarator : Identifier array.dimension [ '=' initializer ] - */ - private Declarator parseDeclarator(SymbolTable tbl, Declarator d) - throws CompileError - { - if (lex.get() != Identifier || d.getType() == VOID) - throw new SyntaxError(lex); - - String name = lex.getString(); - Symbol symbol = new Symbol(name); - int dim = parseArrayDimension(); - ASTree init = null; - if (lex.lookAhead() == '=') { - lex.get(); - init = parseInitializer(tbl); - } - - Declarator decl = d.make(symbol, dim, init); - tbl.append(name, decl); - return decl; - } - - /* initializer : expression | array.initializer - */ - private ASTree parseInitializer(SymbolTable tbl) throws CompileError { - if (lex.lookAhead() == '{') - return parseArrayInitializer(tbl); - else - return parseExpression(tbl); - } - - /* array.initializer : - * '{' (( array.initializer | expression ) ',')* '}' - */ - private ArrayInit parseArrayInitializer(SymbolTable tbl) - throws CompileError - { - lex.get(); // '{' - ASTree expr = parseExpression(tbl); - ArrayInit init = new ArrayInit(expr); - while (lex.lookAhead() == ',') { - lex.get(); - expr = parseExpression(tbl); - ASTList.append(init, expr); - } - - if (lex.get() != '}') - throw new SyntaxError(lex); - - return init; - } - - /* par.expression : '(' expression ')' - */ - private ASTree parseParExpression(SymbolTable tbl) throws CompileError { - if (lex.get() != '(') - throw new SyntaxError(lex); - - ASTree expr = parseExpression(tbl); - if (lex.get() != ')') - throw new SyntaxError(lex); - - return expr; - } - - /* expression : conditional.expr - * | conditional.expr assign.op expression (right-to-left) - */ - public ASTree parseExpression(SymbolTable tbl) throws CompileError { - ASTree left = parseConditionalExpr(tbl); - if (!isAssignOp(lex.lookAhead())) - return left; - - int t = lex.get(); - ASTree right = parseExpression(tbl); - return AssignExpr.makeAssign(t, left, right); - } - - private static boolean isAssignOp(int t) { - return t == '=' || t == MOD_E || t == AND_E - || t == MUL_E || t == PLUS_E || t == MINUS_E || t == DIV_E - || t == EXOR_E || t == OR_E || t == LSHIFT_E - || t == RSHIFT_E || t == ARSHIFT_E; - } - - /* conditional.expr (right-to-left) - * : logical.or.expr [ '?' expression ':' conditional.expr ] - */ - private ASTree parseConditionalExpr(SymbolTable tbl) throws CompileError { - ASTree cond = parseBinaryExpr(tbl); - if (lex.lookAhead() == '?') { - lex.get(); - ASTree thenExpr = parseExpression(tbl); - if (lex.get() != ':') - throw new CompileError(": is missing", lex); - - ASTree elseExpr = parseExpression(tbl); - return new CondExpr(cond, thenExpr, elseExpr); - } - else - return cond; - } - - /* logical.or.expr 10 (operator precedence) - * : logical.and.expr - * | logical.or.expr OROR logical.and.expr left-to-right - * - * logical.and.expr 9 - * : inclusive.or.expr - * | logical.and.expr ANDAND inclusive.or.expr - * - * inclusive.or.expr 8 - * : exclusive.or.expr - * | inclusive.or.expr "|" exclusive.or.expr - * - * exclusive.or.expr 7 - * : and.expr - * | exclusive.or.expr "^" and.expr - * - * and.expr 6 - * : equality.expr - * | and.expr "&" equality.expr - * - * equality.expr 5 - * : relational.expr - * | equality.expr (EQ | NEQ) relational.expr - * - * relational.expr 4 - * : shift.expr - * | relational.expr (LE | GE | "<" | ">") shift.expr - * | relational.expr INSTANCEOF class.type ("[" "]")* - * - * shift.expr 3 - * : additive.expr - * | shift.expr (LSHIFT | RSHIFT | ARSHIFT) additive.expr - * - * additive.expr 2 - * : multiply.expr - * | additive.expr ("+" | "-") multiply.expr - * - * multiply.expr 1 - * : unary.expr - * | multiply.expr ("*" | "/" | "%") unary.expr - */ - private ASTree parseBinaryExpr(SymbolTable tbl) throws CompileError { - ASTree expr = parseUnaryExpr(tbl); - for (;;) { - int t = lex.lookAhead(); - int p = getOpPrecedence(t); - if (p == 0) - return expr; - else - expr = binaryExpr2(tbl, expr, p); - } - } - - private ASTree parseInstanceOf(SymbolTable tbl, ASTree expr) - throws CompileError - { - int t = lex.lookAhead(); - if (isBuiltinType(t)) { - lex.get(); // primitive type - int dim = parseArrayDimension(); - return new InstanceOfExpr(t, dim, expr); - } - else { - ASTList name = parseClassType(tbl); - int dim = parseArrayDimension(); - return new InstanceOfExpr(name, dim, expr); - } - } - - private ASTree binaryExpr2(SymbolTable tbl, ASTree expr, int prec) - throws CompileError - { - int t = lex.get(); - if (t == INSTANCEOF) - return parseInstanceOf(tbl, expr); - - ASTree expr2 = parseUnaryExpr(tbl); - for (;;) { - int t2 = lex.lookAhead(); - int p2 = getOpPrecedence(t2); - if (p2 != 0 && prec > p2) - expr2 = binaryExpr2(tbl, expr2, p2); - else - return BinExpr.makeBin(t, expr, expr2); - } - } - - // !"#$%&'( )*+,-./0 12345678 9:;<=>? - private static final int[] binaryOpPrecedence - = { 0, 0, 0, 0, 1, 6, 0, 0, - 0, 1, 2, 0, 2, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4, 0, 4, 0 }; - - private int getOpPrecedence(int c) { - if ('!' <= c && c <= '?') - return binaryOpPrecedence[c - '!']; - else if (c == '^') - return 7; - else if (c == '|') - return 8; - else if (c == ANDAND) - return 9; - else if (c == OROR) - return 10; - else if (c == EQ || c == NEQ) - return 5; - else if (c == LE || c == GE || c == INSTANCEOF) - return 4; - else if (c == LSHIFT || c == RSHIFT || c == ARSHIFT) - return 3; - else - return 0; // not a binary operator - } - - /* unary.expr : "++"|"--" unary.expr - | "+"|"-" unary.expr - | "!"|"~" unary.expr - | cast.expr - | postfix.expr - - unary.expr.not.plus.minus is a unary expression starting without - "+", "-", "++", or "--". - */ - private ASTree parseUnaryExpr(SymbolTable tbl) throws CompileError { - int t; - switch (lex.lookAhead()) { - case '+' : - case '-' : - case PLUSPLUS : - case MINUSMINUS : - case '!' : - case '~' : - t = lex.get(); - if (t == '-') { - int t2 = lex.lookAhead(); - switch (t2) { - case LongConstant : - case IntConstant : - case CharConstant : - lex.get(); - return new IntConst(-lex.getLong(), t2); - case DoubleConstant : - case FloatConstant : - lex.get(); - return new DoubleConst(-lex.getDouble(), t2); - default : - break; - } - } - - return Expr.make(t, parseUnaryExpr(tbl)); - case '(' : - return parseCast(tbl); - default : - return parsePostfix(tbl); - } - } - - /* cast.expr : "(" builtin.type ("[" "]")* ")" unary.expr - | "(" class.type ("[" "]")* ")" unary.expr2 - - unary.expr2 is a unary.expr beginning with "(", NULL, StringL, - Identifier, THIS, SUPER, or NEW. - - Either "(int.class)" or "(String[].class)" is a not cast expression. - */ - private ASTree parseCast(SymbolTable tbl) throws CompileError { - int t = lex.lookAhead(1); - if (isBuiltinType(t) && nextIsBuiltinCast()) { - lex.get(); // '(' - lex.get(); // primitive type - int dim = parseArrayDimension(); - if (lex.get() != ')') - throw new CompileError(") is missing", lex); - - return new CastExpr(t, dim, parseUnaryExpr(tbl)); - } - else if (t == Identifier && nextIsClassCast()) { - lex.get(); // '(' - ASTList name = parseClassType(tbl); - int dim = parseArrayDimension(); - if (lex.get() != ')') - throw new CompileError(") is missing", lex); - - return new CastExpr(name, dim, parseUnaryExpr(tbl)); - } - else - return parsePostfix(tbl); - } - - private boolean nextIsBuiltinCast() { - int t; - int i = 2; - while ((t = lex.lookAhead(i++)) == '[') - if (lex.lookAhead(i++) != ']') - return false; - - return lex.lookAhead(i - 1) == ')'; - } - - private boolean nextIsClassCast() { - int i = nextIsClassType(1); - if (i < 0) - return false; - - int t = lex.lookAhead(i); - if (t != ')') - return false; - - t = lex.lookAhead(i + 1); - return t == '(' || t == NULL || t == StringL - || t == Identifier || t == THIS || t == SUPER || t == NEW - || t == TRUE || t == FALSE || t == LongConstant - || t == IntConstant || t == CharConstant - || t == DoubleConstant || t == FloatConstant; - } - - private int nextIsClassType(int i) { - int t; - while (lex.lookAhead(++i) == '.') - if (lex.lookAhead(++i) != Identifier) - return -1; - - while ((t = lex.lookAhead(i++)) == '[') - if (lex.lookAhead(i++) != ']') - return -1; - - return i - 1; - } - - /* array.dimension : [ "[" "]" ]* - */ - private int parseArrayDimension() throws CompileError { - int arrayDim = 0; - while (lex.lookAhead() == '[') { - ++arrayDim; - lex.get(); - if (lex.get() != ']') - throw new CompileError("] is missing", lex); - } - - return arrayDim; - } - - /* class.type : Identifier ( "." Identifier )* - */ - private ASTList parseClassType(SymbolTable tbl) throws CompileError { - ASTList list = null; - for (;;) { - if (lex.get() != Identifier) - throw new SyntaxError(lex); - - list = ASTList.append(list, new Symbol(lex.getString())); - if (lex.lookAhead() == '.') - lex.get(); - else - break; - } - - return list; - } - - /* postfix.expr : number.literal - * | primary.expr - * | method.expr - * | postfix.expr "++" | "--" - * | postfix.expr "[" array.size "]" - * | postfix.expr "." Identifier - * | postfix.expr ( "[" "]" )* "." CLASS - * | postfix.expr "#" Identifier - * | postfix.expr "." SUPER - * - * "#" is not an operator of regular Java. It separates - * a class name and a member name in an expression for static member - * access. For example, - * java.lang.Integer.toString(3) in regular Java - * can be written like this: - * java.lang.Integer#toString(3) for this compiler. - */ - private ASTree parsePostfix(SymbolTable tbl) throws CompileError { - int token = lex.lookAhead(); - switch (token) { // see also parseUnaryExpr() - case LongConstant : - case IntConstant : - case CharConstant : - lex.get(); - return new IntConst(lex.getLong(), token); - case DoubleConstant : - case FloatConstant : - lex.get(); - return new DoubleConst(lex.getDouble(), token); - default : - break; - } - - String str; - ASTree index; - ASTree expr = parsePrimaryExpr(tbl); - int t; - while (true) { - switch (lex.lookAhead()) { - case '(' : - expr = parseMethodCall(tbl, expr); - break; - case '[' : - if (lex.lookAhead(1) == ']') { - int dim = parseArrayDimension(); - if (lex.get() != '.' || lex.get() != CLASS) - throw new SyntaxError(lex); - - expr = parseDotClass(expr, dim); - } - else { - index = parseArrayIndex(tbl); - if (index == null) - throw new SyntaxError(lex); - - expr = Expr.make(ARRAY, expr, index); - } - break; - case PLUSPLUS : - case MINUSMINUS : - t = lex.get(); - expr = Expr.make(t, null, expr); - break; - case '.' : - lex.get(); - t = lex.get(); - if (t == CLASS) - expr = parseDotClass(expr, 0); - else if (t == SUPER) - expr = Expr.make('.', new Symbol(toClassName(expr)), new Keyword(t)); - else if (t == Identifier) { - str = lex.getString(); - expr = Expr.make('.', expr, new Member(str)); - } - else - throw new CompileError("missing member name", lex); - break; - case '#' : - lex.get(); - t = lex.get(); - if (t != Identifier) - throw new CompileError("missing static member name", lex); - - str = lex.getString(); - expr = Expr.make(MEMBER, new Symbol(toClassName(expr)), - new Member(str)); - break; - default : - return expr; - } - } - } - - /* Parse a .class expression on a class type. For example, - * String.class => ('.' "String" "class") - * String[].class => ('.' "[LString;" "class") - */ - private ASTree parseDotClass(ASTree className, int dim) - throws CompileError - { - String cname = toClassName(className); - if (dim > 0) { - StringBuffer sbuf = new StringBuffer(); - while (dim-- > 0) - sbuf.append('['); - - sbuf.append('L').append(cname.replace('.', '/')).append(';'); - cname = sbuf.toString(); - } - - return Expr.make('.', new Symbol(cname), new Member("class")); - } - - /* Parses a .class expression on a built-in type. For example, - * int.class => ('#' "java.lang.Integer" "TYPE") - * int[].class => ('.' "[I", "class") - */ - private ASTree parseDotClass(int builtinType, int dim) - throws CompileError - { - if (dim > 0) { - String cname = CodeGen.toJvmTypeName(builtinType, dim); - return Expr.make('.', new Symbol(cname), new Member("class")); - } - else { - String cname; - switch(builtinType) { - case BOOLEAN : - cname = "java.lang.Boolean"; - break; - case BYTE : - cname = "java.lang.Byte"; - break; - case CHAR : - cname = "java.lang.Character"; - break; - case SHORT : - cname = "java.lang.Short"; - break; - case INT : - cname = "java.lang.Integer"; - break; - case LONG : - cname = "java.lang.Long"; - break; - case FLOAT : - cname = "java.lang.Float"; - break; - case DOUBLE : - cname = "java.lang.Double"; - break; - case VOID : - cname = "java.lang.Void"; - break; - default : - throw new CompileError("invalid builtin type: " - + builtinType); - } - - return Expr.make(MEMBER, new Symbol(cname), new Member("TYPE")); - } - } - - /* method.call : method.expr "(" argument.list ")" - * method.expr : THIS | SUPER | Identifier - * | postfix.expr "." Identifier - * | postfix.expr "#" Identifier - */ - private ASTree parseMethodCall(SymbolTable tbl, ASTree expr) - throws CompileError - { - if (expr instanceof Keyword) { - int token = ((Keyword)expr).get(); - if (token != THIS && token != SUPER) - throw new SyntaxError(lex); - } - else if (expr instanceof Symbol) // Identifier - ; - else if (expr instanceof Expr) { - int op = ((Expr)expr).getOperator(); - if (op != '.' && op != MEMBER) - throw new SyntaxError(lex); - } - - return CallExpr.makeCall(expr, parseArgumentList(tbl)); - } - - private String toClassName(ASTree name) - throws CompileError - { - StringBuffer sbuf = new StringBuffer(); - toClassName(name, sbuf); - return sbuf.toString(); - } - - private void toClassName(ASTree name, StringBuffer sbuf) - throws CompileError - { - if (name instanceof Symbol) { - sbuf.append(((Symbol)name).get()); - return; - } - else if (name instanceof Expr) { - Expr expr = (Expr)name; - if (expr.getOperator() == '.') { - toClassName(expr.oprand1(), sbuf); - sbuf.append('.'); - toClassName(expr.oprand2(), sbuf); - return; - } - } - - throw new CompileError("bad static member access", lex); - } - - /* primary.expr : THIS | SUPER | TRUE | FALSE | NULL - * | StringL - * | Identifier - * | NEW new.expr - * | "(" expression ")" - * | builtin.type ( "[" "]" )* "." CLASS - * - * Identifier represents either a local variable name, a member name, - * or a class name. - */ - private ASTree parsePrimaryExpr(SymbolTable tbl) throws CompileError { - int t; - String name; - Declarator decl; - ASTree expr; - - switch (t = lex.get()) { - case THIS : - case SUPER : - case TRUE : - case FALSE : - case NULL : - return new Keyword(t); - case Identifier : - name = lex.getString(); - decl = tbl.lookup(name); - if (decl == null) - return new Member(name); // this or static member - else - return new Variable(name, decl); // local variable - case StringL : - return new StringL(lex.getString()); - case NEW : - return parseNew(tbl); - case '(' : - expr = parseExpression(tbl); - if (lex.get() == ')') - return expr; - else - throw new CompileError(") is missing", lex); - default : - if (isBuiltinType(t) || t == VOID) { - int dim = parseArrayDimension(); - if (lex.get() == '.' && lex.get() == CLASS) - return parseDotClass(t, dim); - } - - throw new SyntaxError(lex); - } - } - - /* new.expr : class.type "(" argument.list ")" - * | class.type array.size [ array.initializer ] - * | primitive.type array.size [ array.initializer ] - */ - private NewExpr parseNew(SymbolTable tbl) throws CompileError { - ArrayInit init = null; - int t = lex.lookAhead(); - if (isBuiltinType(t)) { - lex.get(); - ASTList size = parseArraySize(tbl); - if (lex.lookAhead() == '{') - init = parseArrayInitializer(tbl); - - return new NewExpr(t, size, init); - } - else if (t == Identifier) { - ASTList name = parseClassType(tbl); - t = lex.lookAhead(); - if (t == '(') { - ASTList args = parseArgumentList(tbl); - return new NewExpr(name, args); - } - else if (t == '[') { - ASTList size = parseArraySize(tbl); - if (lex.lookAhead() == '{') - init = parseArrayInitializer(tbl); - - return NewExpr.makeObjectArray(name, size, init); - } - } - - throw new SyntaxError(lex); - } - - /* array.size : [ array.index ]* - */ - private ASTList parseArraySize(SymbolTable tbl) throws CompileError { - ASTList list = null; - while (lex.lookAhead() == '[') - list = ASTList.append(list, parseArrayIndex(tbl)); - - return list; - } - - /* array.index : "[" [ expression ] "]" - */ - private ASTree parseArrayIndex(SymbolTable tbl) throws CompileError { - lex.get(); // '[' - if (lex.lookAhead() == ']') { - lex.get(); - return null; - } - else { - ASTree index = parseExpression(tbl); - if (lex.get() != ']') - throw new CompileError("] is missing", lex); - - return index; - } - } - - /* argument.list : "(" [ expression [ "," expression ]* ] ")" - */ - private ASTList parseArgumentList(SymbolTable tbl) throws CompileError { - if (lex.get() != '(') - throw new CompileError("( is missing", lex); - - ASTList list = null; - if (lex.lookAhead() != ')') - for (;;) { - list = ASTList.append(list, parseExpression(tbl)); - if (lex.lookAhead() == ',') - lex.get(); - else - break; - } - - if (lex.get() != ')') - throw new CompileError(") is missing", lex); - - return list; - } -} - diff --git a/src/com/wenshuo/agent/javassist/compiler/ProceedHandler.java b/src/com/wenshuo/agent/javassist/compiler/ProceedHandler.java deleted file mode 100644 index 7cb7ef4..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ProceedHandler.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.bytecode.Bytecode; -import com.wenshuo.agent.javassist.compiler.ast.ASTList; - -/** - * An interface to an object for implementing $proceed(). - * - * @see javassist.compiler.JvstCodeGen#setProceedHandler(ProceedHandler, String) - */ -public interface ProceedHandler { - void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError; - void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError; -} diff --git a/src/com/wenshuo/agent/javassist/compiler/SymbolTable.java b/src/com/wenshuo/agent/javassist/compiler/SymbolTable.java deleted file mode 100644 index 1a3edc0..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/SymbolTable.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import java.util.HashMap; -import com.wenshuo.agent.javassist.compiler.ast.Declarator; - -public final class SymbolTable extends HashMap { - private SymbolTable parent; - - public SymbolTable() { this(null); } - - public SymbolTable(SymbolTable p) { - super(); - parent = p; - } - - public SymbolTable getParent() { return parent; } - - public Declarator lookup(String name) { - Declarator found = (Declarator)get(name); - if (found == null && parent != null) - return parent.lookup(name); - else - return found; - } - - public void append(String name, Declarator value) { - put(name, value); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/SyntaxError.java b/src/com/wenshuo/agent/javassist/compiler/SyntaxError.java deleted file mode 100644 index 5a6f4b4..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/SyntaxError.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -public class SyntaxError extends CompileError { - public SyntaxError(Lex lexer) { - super("syntax error near \"" + lexer.getTextAround() + "\"", lexer); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/TokenId.java b/src/com/wenshuo/agent/javassist/compiler/TokenId.java deleted file mode 100644 index 58538a6..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/TokenId.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -public interface TokenId { - int ABSTRACT = 300; - int BOOLEAN = 301; - int BREAK = 302; - int BYTE = 303; - int CASE = 304; - int CATCH = 305; - int CHAR = 306; - int CLASS = 307; - int CONST = 308; // reserved keyword - int CONTINUE = 309; - int DEFAULT = 310; - int DO = 311; - int DOUBLE = 312; - int ELSE = 313; - int EXTENDS = 314; - int FINAL = 315; - int FINALLY = 316; - int FLOAT = 317; - int FOR = 318; - int GOTO = 319; // reserved keyword - int IF = 320; - int IMPLEMENTS = 321; - int IMPORT = 322; - int INSTANCEOF = 323; - int INT = 324; - int INTERFACE = 325; - int LONG = 326; - int NATIVE = 327; - int NEW = 328; - int PACKAGE = 329; - int PRIVATE = 330; - int PROTECTED = 331; - int PUBLIC = 332; - int RETURN = 333; - int SHORT = 334; - int STATIC = 335; - int SUPER = 336; - int SWITCH = 337; - int SYNCHRONIZED = 338; - int THIS = 339; - int THROW = 340; - int THROWS = 341; - int TRANSIENT = 342; - int TRY = 343; - int VOID = 344; - int VOLATILE = 345; - int WHILE = 346; - int STRICT = 347; - - int NEQ = 350; // != - int MOD_E = 351; // %= - int AND_E = 352; // &= - int MUL_E = 353; // *= - int PLUS_E = 354; // += - int MINUS_E = 355; // -= - int DIV_E = 356; // /= - int LE = 357; // <= - int EQ = 358; // == - int GE = 359; // >= - int EXOR_E = 360; // ^= - int OR_E = 361; // |= - int PLUSPLUS = 362; // ++ - int MINUSMINUS = 363; // -- - int LSHIFT = 364; // << - int LSHIFT_E = 365; // <<= - int RSHIFT = 366; // >> - int RSHIFT_E = 367; // >>= - int OROR = 368; // || - int ANDAND = 369; // && - int ARSHIFT = 370; // >>> - int ARSHIFT_E = 371; // >>>= - - // operators from NEQ to ARSHIFT_E - String opNames[] = { "!=", "%=", "&=", "*=", "+=", "-=", "/=", - "<=", "==", ">=", "^=", "|=", "++", "--", - "<<", "<<=", ">>", ">>=", "||", "&&", ">>>", - ">>>=" }; - - // operators from MOD_E to ARSHIFT_E - int assignOps[] = { '%', '&', '*', '+', '-', '/', 0, 0, 0, - '^', '|', 0, 0, 0, LSHIFT, 0, RSHIFT, 0, 0, 0, - ARSHIFT }; - - int Identifier = 400; - int CharConstant = 401; - int IntConstant = 402; - int LongConstant = 403; - int FloatConstant = 404; - int DoubleConstant = 405; - int StringL = 406; - - int TRUE = 410; - int FALSE = 411; - int NULL = 412; - - int CALL = 'C'; // method call - int ARRAY = 'A'; // array access - int MEMBER = '#'; // static member access - - int EXPR = 'E'; // expression statement - int LABEL = 'L'; // label statement - int BLOCK = 'B'; // block statement - int DECL = 'D'; // declaration statement - - int BadToken = 500; -} diff --git a/src/com/wenshuo/agent/javassist/compiler/TypeChecker.java b/src/com/wenshuo/agent/javassist/compiler/TypeChecker.java deleted file mode 100644 index 80153d0..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/TypeChecker.java +++ /dev/null @@ -1,1044 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtField; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.Modifier; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.compiler.ast.*; -import com.wenshuo.agent.javassist.bytecode.*; - -public class TypeChecker extends Visitor implements Opcode, TokenId { - static final String javaLangObject = "java.lang.Object"; - static final String jvmJavaLangObject = "java/lang/Object"; - static final String jvmJavaLangString = "java/lang/String"; - static final String jvmJavaLangClass = "java/lang/Class"; - - /* The following fields are used by atXXX() methods - * for returning the type of the compiled expression. - */ - protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ... - protected int arrayDim; - protected String className; // JVM-internal representation - - protected MemberResolver resolver; - protected CtClass thisClass; - protected MethodInfo thisMethod; - - public TypeChecker(CtClass cc, ClassPool cp) { - resolver = new MemberResolver(cp); - thisClass = cc; - thisMethod = null; - } - - /* - * Converts an array of tuples of exprType, arrayDim, and className - * into a String object. - */ - protected static String argTypesToString(int[] types, int[] dims, - String[] cnames) { - StringBuffer sbuf = new StringBuffer(); - sbuf.append('('); - int n = types.length; - if (n > 0) { - int i = 0; - while (true) { - typeToString(sbuf, types[i], dims[i], cnames[i]); - if (++i < n) - sbuf.append(','); - else - break; - } - } - - sbuf.append(')'); - return sbuf.toString(); - } - - /* - * Converts a tuple of exprType, arrayDim, and className - * into a String object. - */ - protected static StringBuffer typeToString(StringBuffer sbuf, - int type, int dim, String cname) { - String s; - if (type == CLASS) - s = MemberResolver.jvmToJavaName(cname); - else if (type == NULL) - s = "Object"; - else - try { - s = MemberResolver.getTypeName(type); - } - catch (CompileError e) { - s = "?"; - } - - sbuf.append(s); - while (dim-- > 0) - sbuf.append("[]"); - - return sbuf; - } - - /** - * Records the currently compiled method. - */ - public void setThisMethod(MethodInfo m) { - thisMethod = m; - } - - protected static void fatal() throws CompileError { - throw new CompileError("fatal"); - } - - /** - * Returns the JVM-internal representation of this class name. - */ - protected String getThisName() { - return MemberResolver.javaToJvmName(thisClass.getName()); - } - - /** - * Returns the JVM-internal representation of this super class name. - */ - protected String getSuperName() throws CompileError { - return MemberResolver.javaToJvmName( - MemberResolver.getSuperclass(thisClass).getName()); - } - - /* Converts a class name into a JVM-internal representation. - * - * It may also expand a simple class name to java.lang.*. - * For example, this converts Object into java/lang/Object. - */ - protected String resolveClassName(ASTList name) throws CompileError { - return resolver.resolveClassName(name); - } - - /* Expands a simple class name to java.lang.*. - * For example, this converts Object into java/lang/Object. - */ - protected String resolveClassName(String jvmName) throws CompileError { - return resolver.resolveJvmClassName(jvmName); - } - - public void atNewExpr(NewExpr expr) throws CompileError { - if (expr.isArray()) - atNewArrayExpr(expr); - else { - CtClass clazz = resolver.lookupClassByName(expr.getClassName()); - String cname = clazz.getName(); - ASTList args = expr.getArguments(); - atMethodCallCore(clazz, MethodInfo.nameInit, args); - exprType = CLASS; - arrayDim = 0; - className = MemberResolver.javaToJvmName(cname); - } - } - - public void atNewArrayExpr(NewExpr expr) throws CompileError { - int type = expr.getArrayType(); - ASTList size = expr.getArraySize(); - ASTList classname = expr.getClassName(); - ASTree init = expr.getInitializer(); - if (init != null) - init.accept(this); - - if (size.length() > 1) - atMultiNewArray(type, classname, size); - else { - ASTree sizeExpr = size.head(); - if (sizeExpr != null) - sizeExpr.accept(this); - - exprType = type; - arrayDim = 1; - if (type == CLASS) - className = resolveClassName(classname); - else - className = null; - } - } - - public void atArrayInit(ArrayInit init) throws CompileError { - ASTList list = init; - while (list != null) { - ASTree h = list.head(); - list = list.tail(); - if (h != null) - h.accept(this); - } - } - - protected void atMultiNewArray(int type, ASTList classname, ASTList size) - throws CompileError - { - int count, dim; - dim = size.length(); - for (count = 0; size != null; size = size.tail()) { - ASTree s = size.head(); - if (s == null) - break; // int[][][] a = new int[3][4][]; - - ++count; - s.accept(this); - } - - exprType = type; - arrayDim = dim; - if (type == CLASS) - className = resolveClassName(classname); - else - className = null; - } - - public void atAssignExpr(AssignExpr expr) throws CompileError { - // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>= - int op = expr.getOperator(); - ASTree left = expr.oprand1(); - ASTree right = expr.oprand2(); - if (left instanceof Variable) - atVariableAssign(expr, op, (Variable)left, - ((Variable)left).getDeclarator(), - right); - else { - if (left instanceof Expr) { - Expr e = (Expr)left; - if (e.getOperator() == ARRAY) { - atArrayAssign(expr, op, (Expr)left, right); - return; - } - } - - atFieldAssign(expr, op, left, right); - } - } - - /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. - * - * expr and var can be null. - */ - private void atVariableAssign(Expr expr, int op, Variable var, - Declarator d, ASTree right) - throws CompileError - { - int varType = d.getType(); - int varArray = d.getArrayDim(); - String varClass = d.getClassName(); - - if (op != '=') - atVariable(var); - - right.accept(this); - exprType = varType; - arrayDim = varArray; - className = varClass; - } - - private void atArrayAssign(Expr expr, int op, Expr array, - ASTree right) throws CompileError - { - atArrayRead(array.oprand1(), array.oprand2()); - int aType = exprType; - int aDim = arrayDim; - String cname = className; - right.accept(this); - exprType = aType; - arrayDim = aDim; - className = cname; - } - - protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) - throws CompileError - { - CtField f = fieldAccess(left); - atFieldRead(f); - int fType = exprType; - int fDim = arrayDim; - String cname = className; - right.accept(this); - exprType = fType; - arrayDim = fDim; - className = cname; - } - - public void atCondExpr(CondExpr expr) throws CompileError { - booleanExpr(expr.condExpr()); - expr.thenExpr().accept(this); - int type1 = exprType; - int dim1 = arrayDim; - String cname1 = className; - expr.elseExpr().accept(this); - - if (dim1 == 0 && dim1 == arrayDim) - if (CodeGen.rightIsStrong(type1, exprType)) - expr.setThen(new CastExpr(exprType, 0, expr.thenExpr())); - else if (CodeGen.rightIsStrong(exprType, type1)) { - expr.setElse(new CastExpr(type1, 0, expr.elseExpr())); - exprType = type1; - } - } - - /* - * If atBinExpr() substitutes a new expression for the original - * binary-operator expression, it changes the operator name to '+' - * (if the original is not '+') and sets the new expression to the - * left-hand-side expression and null to the right-hand-side expression. - */ - public void atBinExpr(BinExpr expr) throws CompileError { - int token = expr.getOperator(); - int k = CodeGen.lookupBinOp(token); - if (k >= 0) { - /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>> - */ - if (token == '+') { - Expr e = atPlusExpr(expr); - if (e != null) { - /* String concatenation has been translated into - * an expression using StringBuffer. - */ - e = CallExpr.makeCall(Expr.make('.', e, - new Member("toString")), null); - expr.setOprand1(e); - expr.setOprand2(null); // <---- look at this! - className = jvmJavaLangString; - } - } - else { - ASTree left = expr.oprand1(); - ASTree right = expr.oprand2(); - left.accept(this); - int type1 = exprType; - right.accept(this); - if (!isConstant(expr, token, left, right)) - computeBinExprType(expr, token, type1); - } - } - else { - /* equation: &&, ||, ==, !=, <=, >=, <, > - */ - booleanExpr(expr); - } - } - - /* EXPR must be a + expression. - * atPlusExpr() returns non-null if the given expression is string - * concatenation. The returned value is "new StringBuffer().append..". - */ - private Expr atPlusExpr(BinExpr expr) throws CompileError { - ASTree left = expr.oprand1(); - ASTree right = expr.oprand2(); - if (right == null) { - // this expression has been already type-checked. - // see atBinExpr() above. - left.accept(this); - return null; - } - - if (isPlusExpr(left)) { - Expr newExpr = atPlusExpr((BinExpr)left); - if (newExpr != null) { - right.accept(this); - exprType = CLASS; - arrayDim = 0; - className = "java/lang/StringBuffer"; - return makeAppendCall(newExpr, right); - } - } - else - left.accept(this); - - int type1 = exprType; - int dim1 = arrayDim; - String cname = className; - right.accept(this); - - if (isConstant(expr, '+', left, right)) - return null; - - if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname)) - || (exprType == CLASS && arrayDim == 0 - && jvmJavaLangString.equals(className))) { - ASTList sbufClass = ASTList.make(new Symbol("java"), - new Symbol("lang"), new Symbol("StringBuffer")); - ASTree e = new NewExpr(sbufClass, null); - exprType = CLASS; - arrayDim = 0; - className = "java/lang/StringBuffer"; - return makeAppendCall(makeAppendCall(e, left), right); - } - else { - computeBinExprType(expr, '+', type1); - return null; - } - } - - private boolean isConstant(BinExpr expr, int op, ASTree left, - ASTree right) throws CompileError - { - left = stripPlusExpr(left); - right = stripPlusExpr(right); - ASTree newExpr = null; - if (left instanceof StringL && right instanceof StringL && op == '+') - newExpr = new StringL(((StringL)left).get() - + ((StringL)right).get()); - else if (left instanceof IntConst) - newExpr = ((IntConst)left).compute(op, right); - else if (left instanceof DoubleConst) - newExpr = ((DoubleConst)left).compute(op, right); - - if (newExpr == null) - return false; // not a constant expression - else { - expr.setOperator('+'); - expr.setOprand1(newExpr); - expr.setOprand2(null); - newExpr.accept(this); // for setting exprType, arrayDim, ... - return true; - } - } - - /* CodeGen.atSwitchStmnt() also calls stripPlusExpr(). - */ - static ASTree stripPlusExpr(ASTree expr) { - if (expr instanceof BinExpr) { - BinExpr e = (BinExpr)expr; - if (e.getOperator() == '+' && e.oprand2() == null) - return e.getLeft(); - } - else if (expr instanceof Expr) { // note: BinExpr extends Expr. - Expr e = (Expr)expr; - int op = e.getOperator(); - if (op == MEMBER) { - ASTree cexpr = getConstantFieldValue((Member)e.oprand2()); - if (cexpr != null) - return cexpr; - } - else if (op == '+' && e.getRight() == null) - return e.getLeft(); - } - else if (expr instanceof Member) { - ASTree cexpr = getConstantFieldValue((Member)expr); - if (cexpr != null) - return cexpr; - } - - return expr; - } - - /** - * If MEM is a static final field, this method returns a constant - * expression representing the value of that field. - */ - private static ASTree getConstantFieldValue(Member mem) { - return getConstantFieldValue(mem.getField()); - } - - public static ASTree getConstantFieldValue(CtField f) { - if (f == null) - return null; - - Object value = f.getConstantValue(); - if (value == null) - return null; - - if (value instanceof String) - return new StringL((String)value); - else if (value instanceof Double || value instanceof Float) { - int token = (value instanceof Double) - ? DoubleConstant : FloatConstant; - return new DoubleConst(((Number)value).doubleValue(), token); - } - else if (value instanceof Number) { - int token = (value instanceof Long) ? LongConstant : IntConstant; - return new IntConst(((Number)value).longValue(), token); - } - else if (value instanceof Boolean) - return new Keyword(((Boolean)value).booleanValue() - ? TokenId.TRUE : TokenId.FALSE); - else - return null; - } - - private static boolean isPlusExpr(ASTree expr) { - if (expr instanceof BinExpr) { - BinExpr bexpr = (BinExpr)expr; - int token = bexpr.getOperator(); - return token == '+'; - } - - return false; - } - - private static Expr makeAppendCall(ASTree target, ASTree arg) { - return CallExpr.makeCall(Expr.make('.', target, new Member("append")), - new ASTList(arg)); - } - - private void computeBinExprType(BinExpr expr, int token, int type1) - throws CompileError - { - // arrayDim should be 0. - int type2 = exprType; - if (token == LSHIFT || token == RSHIFT || token == ARSHIFT) - exprType = type1; - else - insertCast(expr, type1, type2); - - if (CodeGen.isP_INT(exprType)) - exprType = INT; // type1 may be BYTE, ... - } - - private void booleanExpr(ASTree expr) - throws CompileError - { - int op = CodeGen.getCompOperator(expr); - if (op == EQ) { // ==, !=, ... - BinExpr bexpr = (BinExpr)expr; - bexpr.oprand1().accept(this); - int type1 = exprType; - int dim1 = arrayDim; - bexpr.oprand2().accept(this); - if (dim1 == 0 && arrayDim == 0) - insertCast(bexpr, type1, exprType); - } - else if (op == '!') - ((Expr)expr).oprand1().accept(this); - else if (op == ANDAND || op == OROR) { - BinExpr bexpr = (BinExpr)expr; - bexpr.oprand1().accept(this); - bexpr.oprand2().accept(this); - } - else // others - expr.accept(this); - - exprType = BOOLEAN; - arrayDim = 0; - } - - private void insertCast(BinExpr expr, int type1, int type2) - throws CompileError - { - if (CodeGen.rightIsStrong(type1, type2)) - expr.setLeft(new CastExpr(type2, 0, expr.oprand1())); - else - exprType = type1; - } - - public void atCastExpr(CastExpr expr) throws CompileError { - String cname = resolveClassName(expr.getClassName()); - expr.getOprand().accept(this); - exprType = expr.getType(); - arrayDim = expr.getArrayDim(); - className = cname; - } - - public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { - expr.getOprand().accept(this); - exprType = BOOLEAN; - arrayDim = 0; - } - - public void atExpr(Expr expr) throws CompileError { - // array access, member access, - // (unary) +, (unary) -, ++, --, !, ~ - - int token = expr.getOperator(); - ASTree oprand = expr.oprand1(); - if (token == '.') { - String member = ((Symbol)expr.oprand2()).get(); - if (member.equals("length")) - try { - atArrayLength(expr); - } - catch (NoFieldException nfe) { - // length might be a class or package name. - atFieldRead(expr); - } - else if (member.equals("class")) - atClassObject(expr); // .class - else - atFieldRead(expr); - } - else if (token == MEMBER) { // field read - String member = ((Symbol)expr.oprand2()).get(); - if (member.equals("class")) - atClassObject(expr); // .class - else - atFieldRead(expr); - } - else if (token == ARRAY) - atArrayRead(oprand, expr.oprand2()); - else if (token == PLUSPLUS || token == MINUSMINUS) - atPlusPlus(token, oprand, expr); - else if (token == '!') - booleanExpr(expr); - else if (token == CALL) // method call - fatal(); - else { - oprand.accept(this); - if (!isConstant(expr, token, oprand)) - if (token == '-' || token == '~') - if (CodeGen.isP_INT(exprType)) - exprType = INT; // type may be BYTE, ... - } - } - - private boolean isConstant(Expr expr, int op, ASTree oprand) { - oprand = stripPlusExpr(oprand); - if (oprand instanceof IntConst) { - IntConst c = (IntConst)oprand; - long v = c.get(); - if (op == '-') - v = -v; - else if (op == '~') - v = ~v; - else - return false; - - c.set(v); - } - else if (oprand instanceof DoubleConst) { - DoubleConst c = (DoubleConst)oprand; - if (op == '-') - c.set(-c.get()); - else - return false; - } - else - return false; - - expr.setOperator('+'); - return true; - } - - public void atCallExpr(CallExpr expr) throws CompileError { - String mname = null; - CtClass targetClass = null; - ASTree method = expr.oprand1(); - ASTList args = (ASTList)expr.oprand2(); - - if (method instanceof Member) { - mname = ((Member)method).get(); - targetClass = thisClass; - } - else if (method instanceof Keyword) { // constructor - mname = MethodInfo.nameInit; // - if (((Keyword)method).get() == SUPER) - targetClass = MemberResolver.getSuperclass(thisClass); - else - targetClass = thisClass; - } - else if (method instanceof Expr) { - Expr e = (Expr)method; - mname = ((Symbol)e.oprand2()).get(); - int op = e.getOperator(); - if (op == MEMBER) // static method - targetClass - = resolver.lookupClass(((Symbol)e.oprand1()).get(), - false); - else if (op == '.') { - ASTree target = e.oprand1(); - String classFollowedByDotSuper = isDotSuper(target); - if (classFollowedByDotSuper != null) - targetClass = MemberResolver.getSuperInterface(thisClass, - classFollowedByDotSuper); - else { - try { - target.accept(this); - } - catch (NoFieldException nfe) { - if (nfe.getExpr() != target) - throw nfe; - - // it should be a static method. - exprType = CLASS; - arrayDim = 0; - className = nfe.getField(); // JVM-internal - e.setOperator(MEMBER); - e.setOprand1(new Symbol(MemberResolver.jvmToJavaName( - className))); - } - - if (arrayDim > 0) - targetClass = resolver.lookupClass(javaLangObject, true); - else if (exprType == CLASS /* && arrayDim == 0 */) - targetClass = resolver.lookupClassByJvmName(className); - else - badMethod(); - } - } - else - badMethod(); - } - else - fatal(); - - MemberResolver.Method minfo - = atMethodCallCore(targetClass, mname, args); - expr.setMethod(minfo); - } - - private static void badMethod() throws CompileError { - throw new CompileError("bad method"); - } - - /** - * Returns non-null if target is something like Foo.super - * for accessing the default method in an interface. - * Otherwise, null. - * - * @return the class name followed by {@code .super} or null. - */ - static String isDotSuper(ASTree target) { - if (target instanceof Expr) { - Expr e = (Expr)target; - if (e.getOperator() == '.') { - ASTree right = e.oprand2(); - if (right instanceof Keyword && ((Keyword)right).get() == SUPER) - return ((Symbol)e.oprand1()).get(); - } - } - - return null; - } - - /** - * @return a pair of the class declaring the invoked method - * and the MethodInfo of that method. Never null. - */ - public MemberResolver.Method atMethodCallCore(CtClass targetClass, - String mname, ASTList args) - throws CompileError - { - int nargs = getMethodArgsLength(args); - int[] types = new int[nargs]; - int[] dims = new int[nargs]; - String[] cnames = new String[nargs]; - atMethodArgs(args, types, dims, cnames); - - MemberResolver.Method found - = resolver.lookupMethod(targetClass, thisClass, thisMethod, - mname, types, dims, cnames); - if (found == null) { - String clazz = targetClass.getName(); - String signature = argTypesToString(types, dims, cnames); - String msg; - if (mname.equals(MethodInfo.nameInit)) - msg = "cannot find constructor " + clazz + signature; - else - msg = mname + signature + " not found in " + clazz; - - throw new CompileError(msg); - } - - String desc = found.info.getDescriptor(); - setReturnType(desc); - return found; - } - - public int getMethodArgsLength(ASTList args) { - return ASTList.length(args); - } - - public void atMethodArgs(ASTList args, int[] types, int[] dims, - String[] cnames) throws CompileError { - int i = 0; - while (args != null) { - ASTree a = args.head(); - a.accept(this); - types[i] = exprType; - dims[i] = arrayDim; - cnames[i] = className; - ++i; - args = args.tail(); - } - } - - void setReturnType(String desc) throws CompileError { - int i = desc.indexOf(')'); - if (i < 0) - badMethod(); - - char c = desc.charAt(++i); - int dim = 0; - while (c == '[') { - ++dim; - c = desc.charAt(++i); - } - - arrayDim = dim; - if (c == 'L') { - int j = desc.indexOf(';', i + 1); - if (j < 0) - badMethod(); - - exprType = CLASS; - className = desc.substring(i + 1, j); - } - else { - exprType = MemberResolver.descToType(c); - className = null; - } - } - - private void atFieldRead(ASTree expr) throws CompileError { - atFieldRead(fieldAccess(expr)); - } - - private void atFieldRead(CtField f) throws CompileError { - FieldInfo finfo = f.getFieldInfo2(); - String type = finfo.getDescriptor(); - - int i = 0; - int dim = 0; - char c = type.charAt(i); - while (c == '[') { - ++dim; - c = type.charAt(++i); - } - - arrayDim = dim; - exprType = MemberResolver.descToType(c); - - if (c == 'L') - className = type.substring(i + 1, type.indexOf(';', i + 1)); - else - className = null; - } - - /* if EXPR is to access a static field, fieldAccess() translates EXPR - * into an expression using '#' (MEMBER). For example, it translates - * java.lang.Integer.TYPE into java.lang.Integer#TYPE. This translation - * speeds up type resolution by MemberCodeGen. - */ - protected CtField fieldAccess(ASTree expr) throws CompileError { - if (expr instanceof Member) { - Member mem = (Member)expr; - String name = mem.get(); - try { - CtField f = thisClass.getField(name); - if (Modifier.isStatic(f.getModifiers())) - mem.setField(f); - - return f; - } - catch (NotFoundException e) { - // EXPR might be part of a static member access? - throw new NoFieldException(name, expr); - } - } - else if (expr instanceof Expr) { - Expr e = (Expr)expr; - int op = e.getOperator(); - if (op == MEMBER) { - Member mem = (Member)e.oprand2(); - CtField f - = resolver.lookupField(((Symbol)e.oprand1()).get(), mem); - mem.setField(f); - return f; - } - else if (op == '.') { - try { - e.oprand1().accept(this); - } - catch (NoFieldException nfe) { - if (nfe.getExpr() != e.oprand1()) - throw nfe; - - /* EXPR should be a static field. - * If EXPR might be part of a qualified class name, - * lookupFieldByJvmName2() throws NoFieldException. - */ - return fieldAccess2(e, nfe.getField()); - } - - CompileError err = null; - try { - if (exprType == CLASS && arrayDim == 0) - return resolver.lookupFieldByJvmName(className, - (Symbol)e.oprand2()); - } - catch (CompileError ce) { - err = ce; - } - - /* If a filed name is the same name as a package's, - * a static member of a class in that package is not - * visible. For example, - * - * class Foo { - * int javassist; - * } - * - * It is impossible to add the following method: - * - * String m() { return javassist.CtClass.intType.toString(); } - * - * because javassist is a field name. However, this is - * often inconvenient, this compiler allows it. The following - * code is for that. - */ - ASTree oprnd1 = e.oprand1(); - if (oprnd1 instanceof Symbol) - return fieldAccess2(e, ((Symbol)oprnd1).get()); - - if (err != null) - throw err; - } - } - - throw new CompileError("bad filed access"); - } - - private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError { - Member fname = (Member)e.oprand2(); - CtField f = resolver.lookupFieldByJvmName2(jvmClassName, fname, e); - e.setOperator(MEMBER); - e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(jvmClassName))); - fname.setField(f); - return f; - } - - public void atClassObject(Expr expr) throws CompileError { - exprType = CLASS; - arrayDim = 0; - className =jvmJavaLangClass; - } - - public void atArrayLength(Expr expr) throws CompileError { - expr.oprand1().accept(this); - if (arrayDim == 0) - throw new NoFieldException("length", expr); - - exprType = INT; - arrayDim = 0; - } - - public void atArrayRead(ASTree array, ASTree index) - throws CompileError - { - array.accept(this); - int type = exprType; - int dim = arrayDim; - String cname = className; - index.accept(this); - exprType = type; - arrayDim = dim - 1; - className = cname; - } - - private void atPlusPlus(int token, ASTree oprand, Expr expr) - throws CompileError - { - boolean isPost = oprand == null; // ++i or i++? - if (isPost) - oprand = expr.oprand2(); - - if (oprand instanceof Variable) { - Declarator d = ((Variable)oprand).getDeclarator(); - exprType = d.getType(); - arrayDim = d.getArrayDim(); - } - else { - if (oprand instanceof Expr) { - Expr e = (Expr)oprand; - if (e.getOperator() == ARRAY) { - atArrayRead(e.oprand1(), e.oprand2()); - // arrayDim should be 0. - int t = exprType; - if (t == INT || t == BYTE || t == CHAR || t == SHORT) - exprType = INT; - - return; - } - } - - atFieldPlusPlus(oprand); - } - } - - protected void atFieldPlusPlus(ASTree oprand) throws CompileError - { - CtField f = fieldAccess(oprand); - atFieldRead(f); - int t = exprType; - if (t == INT || t == BYTE || t == CHAR || t == SHORT) - exprType = INT; - } - - public void atMember(Member mem) throws CompileError { - atFieldRead(mem); - } - - public void atVariable(Variable v) throws CompileError { - Declarator d = v.getDeclarator(); - exprType = d.getType(); - arrayDim = d.getArrayDim(); - className = d.getClassName(); - } - - public void atKeyword(Keyword k) throws CompileError { - arrayDim = 0; - int token = k.get(); - switch (token) { - case TRUE : - case FALSE : - exprType = BOOLEAN; - break; - case NULL : - exprType = NULL; - break; - case THIS : - case SUPER : - exprType = CLASS; - if (token == THIS) - className = getThisName(); - else - className = getSuperName(); - break; - default : - fatal(); - } - } - - public void atStringL(StringL s) throws CompileError { - exprType = CLASS; - arrayDim = 0; - className = jvmJavaLangString; - } - - public void atIntConst(IntConst i) throws CompileError { - arrayDim = 0; - int type = i.getType(); - if (type == IntConstant || type == CharConstant) - exprType = (type == IntConstant ? INT : CHAR); - else - exprType = LONG; - } - - public void atDoubleConst(DoubleConst d) throws CompileError { - arrayDim = 0; - if (d.getType() == DoubleConstant) - exprType = DOUBLE; - else - exprType = FLOAT; - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/ASTList.java b/src/com/wenshuo/agent/javassist/compiler/ast/ASTList.java deleted file mode 100644 index 9c6966d..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/ASTList.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * A linked list. - * The right subtree must be an ASTList object or null. - */ -public class ASTList extends ASTree { - private ASTree left; - private ASTList right; - - public ASTList(ASTree _head, ASTList _tail) { - left = _head; - right = _tail; - } - - public ASTList(ASTree _head) { - left = _head; - right = null; - } - - public static ASTList make(ASTree e1, ASTree e2, ASTree e3) { - return new ASTList(e1, new ASTList(e2, new ASTList(e3))); - } - - public ASTree getLeft() { return left; } - - public ASTree getRight() { return right; } - - public void setLeft(ASTree _left) { left = _left; } - - public void setRight(ASTree _right) { - right = (ASTList)_right; - } - - /** - * Returns the car part of the list. - */ - public ASTree head() { return left; } - - public void setHead(ASTree _head) { - left = _head; - } - - /** - * Returns the cdr part of the list. - */ - public ASTList tail() { return right; } - - public void setTail(ASTList _tail) { - right = _tail; - } - - public void accept(Visitor v) throws CompileError { v.atASTList(this); } - - public String toString() { - StringBuffer sbuf = new StringBuffer(); - sbuf.append("(<"); - sbuf.append(getTag()); - sbuf.append('>'); - ASTList list = this; - while (list != null) { - sbuf.append(' '); - ASTree a = list.left; - sbuf.append(a == null ? "" : a.toString()); - list = list.right; - } - - sbuf.append(')'); - return sbuf.toString(); - } - - /** - * Returns the number of the elements in this list. - */ - public int length() { - return length(this); - } - - public static int length(ASTList list) { - if (list == null) - return 0; - - int n = 0; - while (list != null) { - list = list.right; - ++n; - } - - return n; - } - - /** - * Returns a sub list of the list. The sub list begins with the - * n-th element of the list. - * - * @param nth zero or more than zero. - */ - public ASTList sublist(int nth) { - ASTList list = this; - while (nth-- > 0) - list = list.right; - - return list; - } - - /** - * Substitutes newObj for oldObj in the - * list. - */ - public boolean subst(ASTree newObj, ASTree oldObj) { - for (ASTList list = this; list != null; list = list.right) - if (list.left == oldObj) { - list.left = newObj; - return true; - } - - return false; - } - - /** - * Appends an object to a list. - */ - public static ASTList append(ASTList a, ASTree b) { - return concat(a, new ASTList(b)); - } - - /** - * Concatenates two lists. - */ - public static ASTList concat(ASTList a, ASTList b) { - if (a == null) - return b; - else { - ASTList list = a; - while (list.right != null) - list = list.right; - - list.right = b; - return a; - } - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/ASTree.java b/src/com/wenshuo/agent/javassist/compiler/ast/ASTree.java deleted file mode 100644 index b464ec0..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/ASTree.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import java.io.Serializable; -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Abstract Syntax Tree. An ASTree object represents a node of - * a binary tree. If the node is a leaf node, both getLeft() - * and getRight() returns null. - */ -public abstract class ASTree implements Serializable { - public ASTree getLeft() { return null; } - - public ASTree getRight() { return null; } - - public void setLeft(ASTree _left) {} - - public void setRight(ASTree _right) {} - - /** - * Is a method for the visitor pattern. It calls - * atXXX() on the given visitor, where - * XXX is the class name of the node object. - */ - public abstract void accept(Visitor v) throws CompileError; - - public String toString() { - StringBuffer sbuf = new StringBuffer(); - sbuf.append('<'); - sbuf.append(getTag()); - sbuf.append('>'); - return sbuf.toString(); - } - - /** - * Returns the type of this node. This method is used by - * toString(). - */ - protected String getTag() { - String name = getClass().getName(); - return name.substring(name.lastIndexOf('.') + 1); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java b/src/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java deleted file mode 100644 index aff9430..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Array initializer such as { 1, 2, 3 }. - */ -public class ArrayInit extends ASTList { - public ArrayInit(ASTree firstElement) { - super(firstElement); - } - - public void accept(Visitor v) throws CompileError { v.atArrayInit(this); } - - public String getTag() { return "array"; } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java b/src/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java deleted file mode 100644 index 5e49831..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Assignment expression. - */ -public class AssignExpr extends Expr { - /* operator must be either of: - * =, %=, &=, *=, +=, -=, /=, ^=, |=, <<=, >>=, >>>= - */ - - private AssignExpr(int op, ASTree _head, ASTList _tail) { - super(op, _head, _tail); - } - - public static AssignExpr makeAssign(int op, ASTree oprand1, - ASTree oprand2) { - return new AssignExpr(op, oprand1, new ASTList(oprand2)); - } - - public void accept(Visitor v) throws CompileError { - v.atAssignExpr(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java b/src/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java deleted file mode 100644 index 41005c1..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Binary expression. - * - *

If the operator is +, the right node might be null. - * See TypeChecker.atBinExpr(). - */ -public class BinExpr extends Expr { - /* operator must be either of: - * ||, &&, |, ^, &, ==, !=, <=, >=, <, >, - * <<, >>, >>>, +, -, *, /, % - */ - - private BinExpr(int op, ASTree _head, ASTList _tail) { - super(op, _head, _tail); - } - - public static BinExpr makeBin(int op, ASTree oprand1, ASTree oprand2) { - return new BinExpr(op, oprand1, new ASTList(oprand2)); - } - - public void accept(Visitor v) throws CompileError { v.atBinExpr(this); } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java b/src/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java deleted file mode 100644 index f2b1fb1..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; -import com.wenshuo.agent.javassist.compiler.TokenId; -import com.wenshuo.agent.javassist.compiler.MemberResolver; - -/** - * Method call expression. - */ -public class CallExpr extends Expr { - private MemberResolver.Method method; // cached result of lookupMethod() - - private CallExpr(ASTree _head, ASTList _tail) { - super(TokenId.CALL, _head, _tail); - method = null; - } - - public void setMethod(MemberResolver.Method m) { - method = m; - } - - public MemberResolver.Method getMethod() { - return method; - } - - public static CallExpr makeCall(ASTree target, ASTree args) { - return new CallExpr(target, new ASTList(args)); - } - - public void accept(Visitor v) throws CompileError { v.atCallExpr(this); } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java b/src/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java deleted file mode 100644 index 60f7b43..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.TokenId; -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Cast expression. - */ -public class CastExpr extends ASTList implements TokenId { - protected int castType; - protected int arrayDim; - - public CastExpr(ASTList className, int dim, ASTree expr) { - super(className, new ASTList(expr)); - castType = CLASS; - arrayDim = dim; - } - - public CastExpr(int type, int dim, ASTree expr) { - super(null, new ASTList(expr)); - castType = type; - arrayDim = dim; - } - - /* Returns CLASS, BOOLEAN, INT, or ... - */ - public int getType() { return castType; } - - public int getArrayDim() { return arrayDim; } - - public ASTList getClassName() { return (ASTList)getLeft(); } - - public ASTree getOprand() { return getRight().getLeft(); } - - public void setOprand(ASTree t) { getRight().setLeft(t); } - - public String getTag() { return "cast:" + castType + ":" + arrayDim; } - - public void accept(Visitor v) throws CompileError { v.atCastExpr(this); } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java b/src/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java deleted file mode 100644 index f11690f..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Conditional expression. - */ -public class CondExpr extends ASTList { - public CondExpr(ASTree cond, ASTree thenp, ASTree elsep) { - super(cond, new ASTList(thenp, new ASTList(elsep))); - } - - public ASTree condExpr() { return head(); } - - public void setCond(ASTree t) { setHead(t); } - - public ASTree thenExpr() { return tail().head(); } - - public void setThen(ASTree t) { tail().setHead(t); } - - public ASTree elseExpr() { return tail().tail().head(); } - - public void setElse(ASTree t) { tail().tail().setHead(t); } - - public String getTag() { return "?:"; } - - public void accept(Visitor v) throws CompileError { v.atCondExpr(this); } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/Declarator.java b/src/com/wenshuo/agent/javassist/compiler/ast/Declarator.java deleted file mode 100644 index be7c1cd..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/Declarator.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.TokenId; -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Variable declarator. - */ -public class Declarator extends ASTList implements TokenId { - protected int varType; - protected int arrayDim; - protected int localVar; - protected String qualifiedClass; // JVM-internal representation - - public Declarator(int type, int dim) { - super(null); - varType = type; - arrayDim = dim; - localVar = -1; - qualifiedClass = null; - } - - public Declarator(ASTList className, int dim) { - super(null); - varType = CLASS; - arrayDim = dim; - localVar = -1; - qualifiedClass = astToClassName(className, '/'); - } - - /* For declaring a pre-defined? local variable. - */ - public Declarator(int type, String jvmClassName, int dim, - int var, Symbol sym) { - super(null); - varType = type; - arrayDim = dim; - localVar = var; - qualifiedClass = jvmClassName; - setLeft(sym); - append(this, null); // initializer - } - - public Declarator make(Symbol sym, int dim, ASTree init) { - Declarator d = new Declarator(this.varType, this.arrayDim + dim); - d.qualifiedClass = this.qualifiedClass; - d.setLeft(sym); - append(d, init); - return d; - } - - /* Returns CLASS, BOOLEAN, BYTE, CHAR, SHORT, INT, LONG, FLOAT, - * or DOUBLE (or VOID) - */ - public int getType() { return varType; } - - public int getArrayDim() { return arrayDim; } - - public void addArrayDim(int d) { arrayDim += d; } - - public String getClassName() { return qualifiedClass; } - - public void setClassName(String s) { qualifiedClass = s; } - - public Symbol getVariable() { return (Symbol)getLeft(); } - - public void setVariable(Symbol sym) { setLeft(sym); } - - public ASTree getInitializer() { - ASTList t = tail(); - if (t != null) - return t.head(); - else - return null; - } - - public void setLocalVar(int n) { localVar = n; } - - public int getLocalVar() { return localVar; } - - public String getTag() { return "decl"; } - - public void accept(Visitor v) throws CompileError { - v.atDeclarator(this); - } - - public static String astToClassName(ASTList name, char sep) { - if (name == null) - return null; - - StringBuffer sbuf = new StringBuffer(); - astToClassName(sbuf, name, sep); - return sbuf.toString(); - } - - private static void astToClassName(StringBuffer sbuf, ASTList name, - char sep) { - for (;;) { - ASTree h = name.head(); - if (h instanceof Symbol) - sbuf.append(((Symbol)h).get()); - else if (h instanceof ASTList) - astToClassName(sbuf, (ASTList)h, sep); - - name = name.tail(); - if (name == null) - break; - - sbuf.append(sep); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java b/src/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java deleted file mode 100644 index 777e3cf..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; -import com.wenshuo.agent.javassist.compiler.TokenId; - -/** - * Double constant. - */ -public class DoubleConst extends ASTree { - protected double value; - protected int type; - - public DoubleConst(double v, int tokenId) { value = v; type = tokenId; } - - public double get() { return value; } - - public void set(double v) { value = v; } - - /* Returns DoubleConstant or FloatConstant - */ - public int getType() { return type; } - - public String toString() { return Double.toString(value); } - - public void accept(Visitor v) throws CompileError { - v.atDoubleConst(this); - } - - public ASTree compute(int op, ASTree right) { - if (right instanceof IntConst) - return compute0(op, (IntConst)right); - else if (right instanceof DoubleConst) - return compute0(op, (DoubleConst)right); - else - return null; - } - - private DoubleConst compute0(int op, DoubleConst right) { - int newType; - if (this.type == TokenId.DoubleConstant - || right.type == TokenId.DoubleConstant) - newType = TokenId.DoubleConstant; - else - newType = TokenId.FloatConstant; - - return compute(op, this.value, right.value, newType); - } - - private DoubleConst compute0(int op, IntConst right) { - return compute(op, this.value, (double)right.value, this.type); - } - - private static DoubleConst compute(int op, double value1, double value2, - int newType) - { - double newValue; - switch (op) { - case '+' : - newValue = value1 + value2; - break; - case '-' : - newValue = value1 - value2; - break; - case '*' : - newValue = value1 * value2; - break; - case '/' : - newValue = value1 / value2; - break; - case '%' : - newValue = value1 % value2; - break; - default : - return null; - } - - return new DoubleConst(newValue, newType); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/Expr.java b/src/com/wenshuo/agent/javassist/compiler/ast/Expr.java deleted file mode 100644 index d49ca3e..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/Expr.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.TokenId; -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Expression. - */ -public class Expr extends ASTList implements TokenId { - /* operator must be either of: - * (unary) +, (unary) -, ++, --, !, ~, - * ARRAY, . (dot), MEMBER (static member access). - * Otherwise, the object should be an instance of a subclass. - */ - - protected int operatorId; - - Expr(int op, ASTree _head, ASTList _tail) { - super(_head, _tail); - operatorId = op; - } - - Expr(int op, ASTree _head) { - super(_head); - operatorId = op; - } - - public static Expr make(int op, ASTree oprand1, ASTree oprand2) { - return new Expr(op, oprand1, new ASTList(oprand2)); - } - - public static Expr make(int op, ASTree oprand1) { - return new Expr(op, oprand1); - } - - public int getOperator() { return operatorId; } - - public void setOperator(int op) { operatorId = op; } - - public ASTree oprand1() { return getLeft(); } - - public void setOprand1(ASTree expr) { - setLeft(expr); - } - - public ASTree oprand2() { return getRight().getLeft(); } - - public void setOprand2(ASTree expr) { - getRight().setLeft(expr); - } - - public void accept(Visitor v) throws CompileError { v.atExpr(this); } - - public String getName() { - int id = operatorId; - if (id < 128) - return String.valueOf((char)id); - else if (NEQ <= id && id <= ARSHIFT_E) - return opNames[id - NEQ]; - else if (id == INSTANCEOF) - return "instanceof"; - else - return String.valueOf(id); - } - - protected String getTag() { - return "op:" + getName(); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java b/src/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java deleted file mode 100644 index 968a97b..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -public class FieldDecl extends ASTList { - public FieldDecl(ASTree _head, ASTList _tail) { - super(_head, _tail); - } - - public ASTList getModifiers() { return (ASTList)getLeft(); } - - public Declarator getDeclarator() { return (Declarator)tail().head(); } - - public ASTree getInit() { return (ASTree)sublist(2).head(); } - - public void accept(Visitor v) throws CompileError { - v.atFieldDecl(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java b/src/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java deleted file mode 100644 index e7fa6d3..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Instanceof expression. - */ -public class InstanceOfExpr extends CastExpr { - public InstanceOfExpr(ASTList className, int dim, ASTree expr) { - super(className, dim, expr); - } - - public InstanceOfExpr(int type, int dim, ASTree expr) { - super(type, dim, expr); - } - - public String getTag() { - return "instanceof:" + castType + ":" + arrayDim; - } - - public void accept(Visitor v) throws CompileError { - v.atInstanceOfExpr(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/IntConst.java b/src/com/wenshuo/agent/javassist/compiler/ast/IntConst.java deleted file mode 100644 index b4d83ca..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/IntConst.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; -import com.wenshuo.agent.javassist.compiler.TokenId; - -/** - * Integer constant. - */ -public class IntConst extends ASTree { - protected long value; - protected int type; - - public IntConst(long v, int tokenId) { value = v; type = tokenId; } - - public long get() { return value; } - - public void set(long v) { value = v; } - - /* Returns IntConstant, CharConstant, or LongConstant. - */ - public int getType() { return type; } - - public String toString() { return Long.toString(value); } - - public void accept(Visitor v) throws CompileError { - v.atIntConst(this); - } - - public ASTree compute(int op, ASTree right) { - if (right instanceof IntConst) - return compute0(op, (IntConst)right); - else if (right instanceof DoubleConst) - return compute0(op, (DoubleConst)right); - else - return null; - } - - private IntConst compute0(int op, IntConst right) { - int type1 = this.type; - int type2 = right.type; - int newType; - if (type1 == TokenId.LongConstant || type2 == TokenId.LongConstant) - newType = TokenId.LongConstant; - else if (type1 == TokenId.CharConstant - && type2 == TokenId.CharConstant) - newType = TokenId.CharConstant; - else - newType = TokenId.IntConstant; - - long value1 = this.value; - long value2 = right.value; - long newValue; - switch (op) { - case '+' : - newValue = value1 + value2; - break; - case '-' : - newValue = value1 - value2; - break; - case '*' : - newValue = value1 * value2; - break; - case '/' : - newValue = value1 / value2; - break; - case '%' : - newValue = value1 % value2; - break; - case '|' : - newValue = value1 | value2; - break; - case '^' : - newValue = value1 ^ value2; - break; - case '&' : - newValue = value1 & value2; - break; - case TokenId.LSHIFT : - newValue = value << (int)value2; - newType = type1; - break; - case TokenId.RSHIFT : - newValue = value >> (int)value2; - newType = type1; - break; - case TokenId.ARSHIFT : - newValue = value >>> (int)value2; - newType = type1; - break; - default : - return null; - } - - return new IntConst(newValue, newType); - } - - private DoubleConst compute0(int op, DoubleConst right) { - double value1 = (double)this.value; - double value2 = right.value; - double newValue; - switch (op) { - case '+' : - newValue = value1 + value2; - break; - case '-' : - newValue = value1 - value2; - break; - case '*' : - newValue = value1 * value2; - break; - case '/' : - newValue = value1 / value2; - break; - case '%' : - newValue = value1 % value2; - break; - default : - return null; - } - - return new DoubleConst(newValue, right.type); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/Keyword.java b/src/com/wenshuo/agent/javassist/compiler/ast/Keyword.java deleted file mode 100644 index d1b63dd..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/Keyword.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Keyword. - */ -public class Keyword extends ASTree { - protected int tokenId; - - public Keyword(int token) { - tokenId = token; - } - - public int get() { return tokenId; } - - public String toString() { return "id:" + tokenId; } - - public void accept(Visitor v) throws CompileError { v.atKeyword(this); } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/Member.java b/src/com/wenshuo/agent/javassist/compiler/ast/Member.java deleted file mode 100644 index cc5c4d1..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/Member.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; -import com.wenshuo.agent.javassist.CtField; - -/** - * Member name. - */ -public class Member extends Symbol { - // cache maintained by fieldAccess() in TypeChecker. - // this is used to obtain the value of a static final field. - private CtField field; - - public Member(String name) { - super(name); - field = null; - } - - public void setField(CtField f) { field = f; } - - public CtField getField() { return field; } - - public void accept(Visitor v) throws CompileError { v.atMember(this); } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java b/src/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java deleted file mode 100644 index a80e374..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -public class MethodDecl extends ASTList { - public static final String initName = ""; - - public MethodDecl(ASTree _head, ASTList _tail) { - super(_head, _tail); - } - - public boolean isConstructor() { - Symbol sym = getReturn().getVariable(); - return sym != null && initName.equals(sym.get()); - } - - public ASTList getModifiers() { return (ASTList)getLeft(); } - - public Declarator getReturn() { return (Declarator)tail().head(); } - - public ASTList getParams() { return (ASTList)sublist(2).head(); } - - public ASTList getThrows() { return (ASTList)sublist(3).head(); } - - public Stmnt getBody() { return (Stmnt)sublist(4).head(); } - - public void accept(Visitor v) throws CompileError { - v.atMethodDecl(this); - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java b/src/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java deleted file mode 100644 index 4b064f2..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.TokenId; -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * New Expression. - */ -public class NewExpr extends ASTList implements TokenId { - protected boolean newArray; - protected int arrayType; - - public NewExpr(ASTList className, ASTList args) { - super(className, new ASTList(args)); - newArray = false; - arrayType = CLASS; - } - - public NewExpr(int type, ASTList arraySize, ArrayInit init) { - super(null, new ASTList(arraySize)); - newArray = true; - arrayType = type; - if (init != null) - append(this, init); - } - - public static NewExpr makeObjectArray(ASTList className, - ASTList arraySize, ArrayInit init) { - NewExpr e = new NewExpr(className, arraySize); - e.newArray = true; - if (init != null) - append(e, init); - - return e; - } - - public boolean isArray() { return newArray; } - - /* TokenId.CLASS, TokenId.INT, ... - */ - public int getArrayType() { return arrayType; } - - public ASTList getClassName() { return (ASTList)getLeft(); } - - public ASTList getArguments() { return (ASTList)getRight().getLeft(); } - - public ASTList getArraySize() { return getArguments(); } - - public ArrayInit getInitializer() { - ASTree t = getRight().getRight(); - if (t == null) - return null; - else - return (ArrayInit)t.getLeft(); - } - - public void accept(Visitor v) throws CompileError { v.atNewExpr(this); } - - protected String getTag() { - return newArray ? "new[]" : "new"; - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/Pair.java b/src/com/wenshuo/agent/javassist/compiler/ast/Pair.java deleted file mode 100644 index 56cb3e6..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/Pair.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * A node of a a binary tree. This class provides concrete methods - * overriding abstract methods in ASTree. - */ -public class Pair extends ASTree { - protected ASTree left, right; - - public Pair(ASTree _left, ASTree _right) { - left = _left; - right = _right; - } - - public void accept(Visitor v) throws CompileError { v.atPair(this); } - - public String toString() { - StringBuffer sbuf = new StringBuffer(); - sbuf.append("( "); - sbuf.append(left == null ? "" : left.toString()); - sbuf.append(" . "); - sbuf.append(right == null ? "" : right.toString()); - sbuf.append(')'); - return sbuf.toString(); - } - - public ASTree getLeft() { return left; } - - public ASTree getRight() { return right; } - - public void setLeft(ASTree _left) { left = _left; } - - public void setRight(ASTree _right) { right = _right; } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java b/src/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java deleted file mode 100644 index 0d81286..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.TokenId; -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Statement. - */ -public class Stmnt extends ASTList implements TokenId { - protected int operatorId; - - public Stmnt(int op, ASTree _head, ASTList _tail) { - super(_head, _tail); - operatorId = op; - } - - public Stmnt(int op, ASTree _head) { - super(_head); - operatorId = op; - } - - public Stmnt(int op) { - this(op, null); - } - - public static Stmnt make(int op, ASTree oprand1, ASTree oprand2) { - return new Stmnt(op, oprand1, new ASTList(oprand2)); - } - - public static Stmnt make(int op, ASTree op1, ASTree op2, ASTree op3) { - return new Stmnt(op, op1, new ASTList(op2, new ASTList(op3))); - } - - public void accept(Visitor v) throws CompileError { v.atStmnt(this); } - - public int getOperator() { return operatorId; } - - protected String getTag() { - if (operatorId < 128) - return "stmnt:" + (char)operatorId; - else - return "stmnt:" + operatorId; - } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/StringL.java b/src/com/wenshuo/agent/javassist/compiler/ast/StringL.java deleted file mode 100644 index d7c43c0..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/StringL.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * String literal. - */ -public class StringL extends ASTree { - protected String text; - - public StringL(String t) { - text = t; - } - - public String get() { return text; } - - public String toString() { return "\"" + text + "\""; } - - public void accept(Visitor v) throws CompileError { v.atStringL(this); } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/Symbol.java b/src/com/wenshuo/agent/javassist/compiler/ast/Symbol.java deleted file mode 100644 index b305cbd..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/Symbol.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Identifier. - */ -public class Symbol extends ASTree { - protected String identifier; - - public Symbol(String sym) { - identifier = sym; - } - - public String get() { return identifier; } - - public String toString() { return identifier; } - - public void accept(Visitor v) throws CompileError { v.atSymbol(this); } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/Variable.java b/src/com/wenshuo/agent/javassist/compiler/ast/Variable.java deleted file mode 100644 index 0b7741a..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/Variable.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * Variable. - */ -public class Variable extends Symbol { - protected Declarator declarator; - - public Variable(String sym, Declarator d) { - super(sym); - declarator = d; - } - - public Declarator getDeclarator() { return declarator; } - - public String toString() { - return identifier + ":" + declarator.getType(); - } - - public void accept(Visitor v) throws CompileError { v.atVariable(this); } -} diff --git a/src/com/wenshuo/agent/javassist/compiler/ast/Visitor.java b/src/com/wenshuo/agent/javassist/compiler/ast/Visitor.java deleted file mode 100644 index e4c6e44..0000000 --- a/src/com/wenshuo/agent/javassist/compiler/ast/Visitor.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.compiler.ast; - -import com.wenshuo.agent.javassist.compiler.CompileError; - -/** - * The visitor pattern. - * - * @see ASTree#accept(Visitor) - */ -public class Visitor { - public void atASTList(ASTList n) throws CompileError {} - public void atPair(Pair n) throws CompileError {} - - public void atFieldDecl(FieldDecl n) throws CompileError {} - public void atMethodDecl(MethodDecl n) throws CompileError {} - public void atStmnt(Stmnt n) throws CompileError {} - public void atDeclarator(Declarator n) throws CompileError {} - - public void atAssignExpr(AssignExpr n) throws CompileError {} - public void atCondExpr(CondExpr n) throws CompileError {} - public void atBinExpr(BinExpr n) throws CompileError {} - public void atExpr(Expr n) throws CompileError {} - public void atCallExpr(CallExpr n) throws CompileError {} - public void atCastExpr(CastExpr n) throws CompileError {} - public void atInstanceOfExpr(InstanceOfExpr n) throws CompileError {} - public void atNewExpr(NewExpr n) throws CompileError {} - - public void atSymbol(Symbol n) throws CompileError {} - public void atMember(Member n) throws CompileError {} - public void atVariable(Variable n) throws CompileError {} - public void atKeyword(Keyword n) throws CompileError {} - public void atStringL(StringL n) throws CompileError {} - public void atIntConst(IntConst n) throws CompileError {} - public void atDoubleConst(DoubleConst n) throws CompileError {} - public void atArrayInit(ArrayInit n) throws CompileError {} -} diff --git a/src/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java b/src/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java deleted file mode 100644 index 3942bd7..0000000 --- a/src/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.CodeConverter.ArrayAccessReplacementMethodNames; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.Descriptor; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; -import com.wenshuo.agent.javassist.bytecode.analysis.Analyzer; -import com.wenshuo.agent.javassist.bytecode.analysis.Frame; - -/** - * A transformer which replaces array access with static method invocations. - * - * @author Kabir Khan - * @author Jason T. Greene - * @version $Revision: 1.8 $ - */ -public final class TransformAccessArrayField extends Transformer { - private final String methodClassname; - private final ArrayAccessReplacementMethodNames names; - private Frame[] frames; - private int offset; - - public TransformAccessArrayField(Transformer next, String methodClassname, - ArrayAccessReplacementMethodNames names) throws NotFoundException { - super(next); - this.methodClassname = methodClassname; - this.names = names; - - } - - public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException { - /* - * This transformer must be isolated from other transformers, since some - * of them affect the local variable and stack maximums without updating - * the code attribute to reflect the changes. This screws up the - * data-flow analyzer, since it relies on consistent code state. Even - * if the attribute values were updated correctly, we would have to - * detect it, and redo analysis, which is not cheap. Instead, we are - * better off doing all changes in initialize() before everyone else has - * a chance to muck things up. - */ - CodeIterator iterator = minfo.getCodeAttribute().iterator(); - while (iterator.hasNext()) { - try { - int pos = iterator.next(); - int c = iterator.byteAt(pos); - - if (c == AALOAD) - initFrames(clazz, minfo); - - if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD - || c == FALOAD || c == IALOAD || c == LALOAD - || c == SALOAD) { - pos = replace(cp, iterator, pos, c, getLoadReplacementSignature(c)); - } else if (c == AASTORE || c == BASTORE || c == CASTORE - || c == DASTORE || c == FASTORE || c == IASTORE - || c == LASTORE || c == SASTORE) { - pos = replace(cp, iterator, pos, c, getStoreReplacementSignature(c)); - } - - } catch (Exception e) { - throw new CannotCompileException(e); - } - } - } - - public void clean() { - frames = null; - offset = -1; - } - - public int transform(CtClass tclazz, int pos, CodeIterator iterator, - ConstPool cp) throws BadBytecode { - // Do nothing, see above comment - return pos; - } - - private Frame getFrame(int pos) throws BadBytecode { - return frames[pos - offset]; // Adjust pos - } - - private void initFrames(CtClass clazz, MethodInfo minfo) throws BadBytecode { - if (frames == null) { - frames = ((new Analyzer())).analyze(clazz, minfo); - offset = 0; // start tracking changes - } - } - - private int updatePos(int pos, int increment) { - if (offset > -1) - offset += increment; - - return pos + increment; - } - - private String getTopType(int pos) throws BadBytecode { - Frame frame = getFrame(pos); - if (frame == null) - return null; - - CtClass clazz = frame.peek().getCtClass(); - return clazz != null ? Descriptor.toJvmName(clazz) : null; - } - - private int replace(ConstPool cp, CodeIterator iterator, int pos, - int opcode, String signature) throws BadBytecode { - String castType = null; - String methodName = getMethodName(opcode); - if (methodName != null) { - // See if the object must be cast - if (opcode == AALOAD) { - castType = getTopType(iterator.lookAhead()); - // Do not replace an AALOAD instruction that we do not have a type for - // This happens when the state is guaranteed to be null (Type.UNINIT) - // So we don't really care about this case. - if (castType == null) - return pos; - if ("java/lang/Object".equals(castType)) - castType = null; - } - - // The gap may include extra padding - // Write a nop in case the padding pushes the instruction forward - iterator.writeByte(NOP, pos); - CodeIterator.Gap gap - = iterator.insertGapAt(pos, castType != null ? 5 : 2, false); - pos = gap.position; - int mi = cp.addClassInfo(methodClassname); - int methodref = cp.addMethodrefInfo(mi, methodName, signature); - iterator.writeByte(INVOKESTATIC, pos); - iterator.write16bit(methodref, pos + 1); - - if (castType != null) { - int index = cp.addClassInfo(castType); - iterator.writeByte(CHECKCAST, pos + 3); - iterator.write16bit(index, pos + 4); - } - - pos = updatePos(pos, gap.length); - } - - return pos; - } - - private String getMethodName(int opcode) { - String methodName = null; - switch (opcode) { - case AALOAD: - methodName = names.objectRead(); - break; - case BALOAD: - methodName = names.byteOrBooleanRead(); - break; - case CALOAD: - methodName = names.charRead(); - break; - case DALOAD: - methodName = names.doubleRead(); - break; - case FALOAD: - methodName = names.floatRead(); - break; - case IALOAD: - methodName = names.intRead(); - break; - case SALOAD: - methodName = names.shortRead(); - break; - case LALOAD: - methodName = names.longRead(); - break; - case AASTORE: - methodName = names.objectWrite(); - break; - case BASTORE: - methodName = names.byteOrBooleanWrite(); - break; - case CASTORE: - methodName = names.charWrite(); - break; - case DASTORE: - methodName = names.doubleWrite(); - break; - case FASTORE: - methodName = names.floatWrite(); - break; - case IASTORE: - methodName = names.intWrite(); - break; - case SASTORE: - methodName = names.shortWrite(); - break; - case LASTORE: - methodName = names.longWrite(); - break; - } - - if (methodName.equals("")) - methodName = null; - - return methodName; - } - - private String getLoadReplacementSignature(int opcode) throws BadBytecode { - switch (opcode) { - case AALOAD: - return "(Ljava/lang/Object;I)Ljava/lang/Object;"; - case BALOAD: - return "(Ljava/lang/Object;I)B"; - case CALOAD: - return "(Ljava/lang/Object;I)C"; - case DALOAD: - return "(Ljava/lang/Object;I)D"; - case FALOAD: - return "(Ljava/lang/Object;I)F"; - case IALOAD: - return "(Ljava/lang/Object;I)I"; - case SALOAD: - return "(Ljava/lang/Object;I)S"; - case LALOAD: - return "(Ljava/lang/Object;I)J"; - } - - throw new BadBytecode(opcode); - } - - private String getStoreReplacementSignature(int opcode) throws BadBytecode { - switch (opcode) { - case AASTORE: - return "(Ljava/lang/Object;ILjava/lang/Object;)V"; - case BASTORE: - return "(Ljava/lang/Object;IB)V"; - case CASTORE: - return "(Ljava/lang/Object;IC)V"; - case DASTORE: - return "(Ljava/lang/Object;ID)V"; - case FASTORE: - return "(Ljava/lang/Object;IF)V"; - case IASTORE: - return "(Ljava/lang/Object;II)V"; - case SASTORE: - return "(Ljava/lang/Object;IS)V"; - case LASTORE: - return "(Ljava/lang/Object;IJ)V"; - } - - throw new BadBytecode(opcode); - } -} diff --git a/src/com/wenshuo/agent/javassist/convert/TransformAfter.java b/src/com/wenshuo/agent/javassist/convert/TransformAfter.java deleted file mode 100644 index ebe8c61..0000000 --- a/src/com/wenshuo/agent/javassist/convert/TransformAfter.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.*; - -public class TransformAfter extends TransformBefore { - public TransformAfter(Transformer next, - CtMethod origMethod, CtMethod afterMethod) - throws NotFoundException - { - super(next, origMethod, afterMethod); - } - - protected int match2(int pos, CodeIterator iterator) throws BadBytecode { - iterator.move(pos); - iterator.insert(saveCode); - iterator.insert(loadCode); - int p = iterator.insertGap(3); - iterator.setMark(p); - iterator.insert(loadCode); - pos = iterator.next(); - p = iterator.getMark(); - iterator.writeByte(iterator.byteAt(pos), p); - iterator.write16bit(iterator.u16bitAt(pos + 1), p + 1); - iterator.writeByte(INVOKESTATIC, pos); - iterator.write16bit(newIndex, pos + 1); - iterator.move(p); - return iterator.next(); - } -} diff --git a/src/com/wenshuo/agent/javassist/convert/TransformBefore.java b/src/com/wenshuo/agent/javassist/convert/TransformBefore.java deleted file mode 100644 index ff75939..0000000 --- a/src/com/wenshuo/agent/javassist/convert/TransformBefore.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.*; - -public class TransformBefore extends TransformCall { - protected CtClass[] parameterTypes; - protected int locals; - protected int maxLocals; - protected byte[] saveCode, loadCode; - - public TransformBefore(Transformer next, - CtMethod origMethod, CtMethod beforeMethod) - throws NotFoundException - { - super(next, origMethod, beforeMethod); - - // override - methodDescriptor = origMethod.getMethodInfo2().getDescriptor(); - - parameterTypes = origMethod.getParameterTypes(); - locals = 0; - maxLocals = 0; - saveCode = loadCode = null; - } - - public void initialize(ConstPool cp, CodeAttribute attr) { - super.initialize(cp, attr); - locals = 0; - maxLocals = attr.getMaxLocals(); - saveCode = loadCode = null; - } - - protected int match(int c, int pos, CodeIterator iterator, - int typedesc, ConstPool cp) throws BadBytecode - { - if (newIndex == 0) { - String desc = Descriptor.ofParameters(parameterTypes) + 'V'; - desc = Descriptor.insertParameter(classname, desc); - int nt = cp.addNameAndTypeInfo(newMethodname, desc); - int ci = cp.addClassInfo(newClassname); - newIndex = cp.addMethodrefInfo(ci, nt); - constPool = cp; - } - - if (saveCode == null) - makeCode(parameterTypes, cp); - - return match2(pos, iterator); - } - - protected int match2(int pos, CodeIterator iterator) throws BadBytecode { - iterator.move(pos); - iterator.insert(saveCode); - iterator.insert(loadCode); - int p = iterator.insertGap(3); - iterator.writeByte(INVOKESTATIC, p); - iterator.write16bit(newIndex, p + 1); - iterator.insert(loadCode); - return iterator.next(); - } - - public int extraLocals() { return locals; } - - protected void makeCode(CtClass[] paramTypes, ConstPool cp) { - Bytecode save = new Bytecode(cp, 0, 0); - Bytecode load = new Bytecode(cp, 0, 0); - - int var = maxLocals; - int len = (paramTypes == null) ? 0 : paramTypes.length; - load.addAload(var); - makeCode2(save, load, 0, len, paramTypes, var + 1); - save.addAstore(var); - - saveCode = save.get(); - loadCode = load.get(); - } - - private void makeCode2(Bytecode save, Bytecode load, - int i, int n, CtClass[] paramTypes, int var) - { - if (i < n) { - int size = load.addLoad(var, paramTypes[i]); - makeCode2(save, load, i + 1, n, paramTypes, var + size); - save.addStore(var, paramTypes[i]); - } - else - locals = var - maxLocals; - } -} diff --git a/src/com/wenshuo/agent/javassist/convert/TransformCall.java b/src/com/wenshuo/agent/javassist/convert/TransformCall.java deleted file mode 100644 index 317bda3..0000000 --- a/src/com/wenshuo/agent/javassist/convert/TransformCall.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.Modifier; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.*; - -public class TransformCall extends Transformer { - protected String classname, methodname, methodDescriptor; - protected String newClassname, newMethodname; - protected boolean newMethodIsPrivate; - - /* cache */ - protected int newIndex; - protected ConstPool constPool; - - public TransformCall(Transformer next, CtMethod origMethod, - CtMethod substMethod) - { - this(next, origMethod.getName(), substMethod); - classname = origMethod.getDeclaringClass().getName(); - } - - public TransformCall(Transformer next, String oldMethodName, - CtMethod substMethod) - { - super(next); - methodname = oldMethodName; - methodDescriptor = substMethod.getMethodInfo2().getDescriptor(); - classname = newClassname = substMethod.getDeclaringClass().getName(); - newMethodname = substMethod.getName(); - constPool = null; - newMethodIsPrivate = Modifier.isPrivate(substMethod.getModifiers()); - } - - public void initialize(ConstPool cp, CodeAttribute attr) { - if (constPool != cp) - newIndex = 0; - } - - /** - * Modify INVOKEINTERFACE, INVOKESPECIAL, INVOKESTATIC and INVOKEVIRTUAL - * so that a different method is invoked. The class name in the operand - * of these instructions might be a subclass of the target class specified - * by classname. This method transforms the instruction - * in that case unless the subclass overrides the target method. - */ - public int transform(CtClass clazz, int pos, CodeIterator iterator, - ConstPool cp) throws BadBytecode - { - int c = iterator.byteAt(pos); - if (c == INVOKEINTERFACE || c == INVOKESPECIAL - || c == INVOKESTATIC || c == INVOKEVIRTUAL) { - int index = iterator.u16bitAt(pos + 1); - String cname = cp.eqMember(methodname, methodDescriptor, index); - if (cname != null && matchClass(cname, clazz.getClassPool())) { - int ntinfo = cp.getMemberNameAndType(index); - pos = match(c, pos, iterator, - cp.getNameAndTypeDescriptor(ntinfo), cp); - } - } - - return pos; - } - - private boolean matchClass(String name, ClassPool pool) { - if (classname.equals(name)) - return true; - - try { - CtClass clazz = pool.get(name); - CtClass declClazz = pool.get(classname); - if (clazz.subtypeOf(declClazz)) - try { - CtMethod m = clazz.getMethod(methodname, methodDescriptor); - return m.getDeclaringClass().getName().equals(classname); - } - catch (NotFoundException e) { - // maybe the original method has been removed. - return true; - } - } - catch (NotFoundException e) { - return false; - } - - return false; - } - - protected int match(int c, int pos, CodeIterator iterator, - int typedesc, ConstPool cp) throws BadBytecode - { - if (newIndex == 0) { - int nt = cp.addNameAndTypeInfo(cp.addUtf8Info(newMethodname), - typedesc); - int ci = cp.addClassInfo(newClassname); - if (c == INVOKEINTERFACE) - newIndex = cp.addInterfaceMethodrefInfo(ci, nt); - else { - if (newMethodIsPrivate && c == INVOKEVIRTUAL) - iterator.writeByte(INVOKESPECIAL, pos); - - newIndex = cp.addMethodrefInfo(ci, nt); - } - - constPool = cp; - } - - iterator.write16bit(newIndex, pos + 1); - return pos; - } -} diff --git a/src/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java b/src/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java deleted file mode 100644 index 08ed468..0000000 --- a/src/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtField; -import com.wenshuo.agent.javassist.Modifier; - -final public class TransformFieldAccess extends Transformer { - private String newClassname, newFieldname; - private String fieldname; - private CtClass fieldClass; - private boolean isPrivate; - - /* cache */ - private int newIndex; - private ConstPool constPool; - - public TransformFieldAccess(Transformer next, CtField field, - String newClassname, String newFieldname) - { - super(next); - this.fieldClass = field.getDeclaringClass(); - this.fieldname = field.getName(); - this.isPrivate = Modifier.isPrivate(field.getModifiers()); - this.newClassname = newClassname; - this.newFieldname = newFieldname; - this.constPool = null; - } - - public void initialize(ConstPool cp, CodeAttribute attr) { - if (constPool != cp) - newIndex = 0; - } - - /** - * Modify GETFIELD, GETSTATIC, PUTFIELD, and PUTSTATIC so that - * a different field is accessed. The new field must be declared - * in a superclass of the class in which the original field is - * declared. - */ - public int transform(CtClass clazz, int pos, - CodeIterator iterator, ConstPool cp) - { - int c = iterator.byteAt(pos); - if (c == GETFIELD || c == GETSTATIC - || c == PUTFIELD || c == PUTSTATIC) { - int index = iterator.u16bitAt(pos + 1); - String typedesc - = TransformReadField.isField(clazz.getClassPool(), cp, - fieldClass, fieldname, isPrivate, index); - if (typedesc != null) { - if (newIndex == 0) { - int nt = cp.addNameAndTypeInfo(newFieldname, - typedesc); - newIndex = cp.addFieldrefInfo( - cp.addClassInfo(newClassname), nt); - constPool = cp; - } - - iterator.write16bit(newIndex, pos + 1); - } - } - - return pos; - } -} diff --git a/src/com/wenshuo/agent/javassist/convert/TransformNew.java b/src/com/wenshuo/agent/javassist/convert/TransformNew.java deleted file mode 100644 index a85d750..0000000 --- a/src/com/wenshuo/agent/javassist/convert/TransformNew.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CannotCompileException; - -final public class TransformNew extends Transformer { - private int nested; - private String classname, trapClass, trapMethod; - - public TransformNew(Transformer next, - String classname, String trapClass, String trapMethod) { - super(next); - this.classname = classname; - this.trapClass = trapClass; - this.trapMethod = trapMethod; - } - - public void initialize(ConstPool cp, CodeAttribute attr) { - nested = 0; - } - - /** - * Replace a sequence of - * NEW classname - * DUP - * ... - * INVOKESPECIAL - * with - * NOP - * NOP - * ... - * INVOKESTATIC trapMethod in trapClass - */ - public int transform(CtClass clazz, int pos, CodeIterator iterator, - ConstPool cp) throws CannotCompileException - { - int index; - int c = iterator.byteAt(pos); - if (c == NEW) { - index = iterator.u16bitAt(pos + 1); - if (cp.getClassInfo(index).equals(classname)) { - if (iterator.byteAt(pos + 3) != DUP) - throw new CannotCompileException( - "NEW followed by no DUP was found"); - - iterator.writeByte(NOP, pos); - iterator.writeByte(NOP, pos + 1); - iterator.writeByte(NOP, pos + 2); - iterator.writeByte(NOP, pos + 3); - ++nested; - - StackMapTable smt - = (StackMapTable)iterator.get().getAttribute(StackMapTable.tag); - if (smt != null) - smt.removeNew(pos); - - StackMap sm - = (StackMap)iterator.get().getAttribute(StackMap.tag); - if (sm != null) - sm.removeNew(pos); - } - } - else if (c == INVOKESPECIAL) { - index = iterator.u16bitAt(pos + 1); - int typedesc = cp.isConstructor(classname, index); - if (typedesc != 0 && nested > 0) { - int methodref = computeMethodref(typedesc, cp); - iterator.writeByte(INVOKESTATIC, pos); - iterator.write16bit(methodref, pos + 1); - --nested; - } - } - - return pos; - } - - private int computeMethodref(int typedesc, ConstPool cp) { - int classIndex = cp.addClassInfo(trapClass); - int mnameIndex = cp.addUtf8Info(trapMethod); - typedesc = cp.addUtf8Info( - Descriptor.changeReturnType(classname, - cp.getUtf8Info(typedesc))); - return cp.addMethodrefInfo(classIndex, - cp.addNameAndTypeInfo(mnameIndex, typedesc)); - } -} diff --git a/src/com/wenshuo/agent/javassist/convert/TransformNewClass.java b/src/com/wenshuo/agent/javassist/convert/TransformNewClass.java deleted file mode 100644 index 513b8ef..0000000 --- a/src/com/wenshuo/agent/javassist/convert/TransformNewClass.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CannotCompileException; - -final public class TransformNewClass extends Transformer { - private int nested; - private String classname, newClassName; - private int newClassIndex, newMethodNTIndex, newMethodIndex; - - public TransformNewClass(Transformer next, - String classname, String newClassName) { - super(next); - this.classname = classname; - this.newClassName = newClassName; - } - - public void initialize(ConstPool cp, CodeAttribute attr) { - nested = 0; - newClassIndex = newMethodNTIndex = newMethodIndex = 0; - } - - /** - * Modifies a sequence of - * NEW classname - * DUP - * ... - * INVOKESPECIAL classname:method - */ - public int transform(CtClass clazz, int pos, CodeIterator iterator, - ConstPool cp) throws CannotCompileException - { - int index; - int c = iterator.byteAt(pos); - if (c == NEW) { - index = iterator.u16bitAt(pos + 1); - if (cp.getClassInfo(index).equals(classname)) { - if (iterator.byteAt(pos + 3) != DUP) - throw new CannotCompileException( - "NEW followed by no DUP was found"); - - if (newClassIndex == 0) - newClassIndex = cp.addClassInfo(newClassName); - - iterator.write16bit(newClassIndex, pos + 1); - ++nested; - } - } - else if (c == INVOKESPECIAL) { - index = iterator.u16bitAt(pos + 1); - int typedesc = cp.isConstructor(classname, index); - if (typedesc != 0 && nested > 0) { - int nt = cp.getMethodrefNameAndType(index); - if (newMethodNTIndex != nt) { - newMethodNTIndex = nt; - newMethodIndex = cp.addMethodrefInfo(newClassIndex, nt); - } - - iterator.write16bit(newMethodIndex, pos + 1); - --nested; - } - } - - return pos; - } -} diff --git a/src/com/wenshuo/agent/javassist/convert/TransformReadField.java b/src/com/wenshuo/agent/javassist/convert/TransformReadField.java deleted file mode 100644 index d8477c8..0000000 --- a/src/com/wenshuo/agent/javassist/convert/TransformReadField.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtField; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.Modifier; - -public class TransformReadField extends Transformer { - protected String fieldname; - protected CtClass fieldClass; - protected boolean isPrivate; - protected String methodClassname, methodName; - - public TransformReadField(Transformer next, CtField field, - String methodClassname, String methodName) - { - super(next); - this.fieldClass = field.getDeclaringClass(); - this.fieldname = field.getName(); - this.methodClassname = methodClassname; - this.methodName = methodName; - this.isPrivate = Modifier.isPrivate(field.getModifiers()); - } - - static String isField(ClassPool pool, ConstPool cp, CtClass fclass, - String fname, boolean is_private, int index) { - if (!cp.getFieldrefName(index).equals(fname)) - return null; - - try { - CtClass c = pool.get(cp.getFieldrefClassName(index)); - if (c == fclass || (!is_private && isFieldInSuper(c, fclass, fname))) - return cp.getFieldrefType(index); - } - catch (NotFoundException e) {} - return null; - } - - static boolean isFieldInSuper(CtClass clazz, CtClass fclass, String fname) { - if (!clazz.subclassOf(fclass)) - return false; - - try { - CtField f = clazz.getField(fname); - return f.getDeclaringClass() == fclass; - } - catch (NotFoundException e) {} - return false; - } - - public int transform(CtClass tclazz, int pos, CodeIterator iterator, - ConstPool cp) throws BadBytecode - { - int c = iterator.byteAt(pos); - if (c == GETFIELD || c == GETSTATIC) { - int index = iterator.u16bitAt(pos + 1); - String typedesc = isField(tclazz.getClassPool(), cp, - fieldClass, fieldname, isPrivate, index); - if (typedesc != null) { - if (c == GETSTATIC) { - iterator.move(pos); - pos = iterator.insertGap(1); // insertGap() may insert 4 bytes. - iterator.writeByte(ACONST_NULL, pos); - pos = iterator.next(); - } - - String type = "(Ljava/lang/Object;)" + typedesc; - int mi = cp.addClassInfo(methodClassname); - int methodref = cp.addMethodrefInfo(mi, methodName, type); - iterator.writeByte(INVOKESTATIC, pos); - iterator.write16bit(methodref, pos + 1); - return pos; - } - } - - return pos; - } -} diff --git a/src/com/wenshuo/agent/javassist/convert/TransformWriteField.java b/src/com/wenshuo/agent/javassist/convert/TransformWriteField.java deleted file mode 100644 index ecc3f38..0000000 --- a/src/com/wenshuo/agent/javassist/convert/TransformWriteField.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtField; -import com.wenshuo.agent.javassist.bytecode.*; - -final public class TransformWriteField extends TransformReadField { - public TransformWriteField(Transformer next, CtField field, - String methodClassname, String methodName) - { - super(next, field, methodClassname, methodName); - } - - public int transform(CtClass tclazz, int pos, CodeIterator iterator, - ConstPool cp) throws BadBytecode - { - int c = iterator.byteAt(pos); - if (c == PUTFIELD || c == PUTSTATIC) { - int index = iterator.u16bitAt(pos + 1); - String typedesc = isField(tclazz.getClassPool(), cp, - fieldClass, fieldname, isPrivate, index); - if (typedesc != null) { - if (c == PUTSTATIC) { - CodeAttribute ca = iterator.get(); - iterator.move(pos); - char c0 = typedesc.charAt(0); - if (c0 == 'J' || c0 == 'D') { // long or double - // insertGap() may insert 4 bytes. - pos = iterator.insertGap(3); - iterator.writeByte(ACONST_NULL, pos); - iterator.writeByte(DUP_X2, pos + 1); - iterator.writeByte(POP, pos + 2); - ca.setMaxStack(ca.getMaxStack() + 2); - } - else { - // insertGap() may insert 4 bytes. - pos = iterator.insertGap(2); - iterator.writeByte(ACONST_NULL, pos); - iterator.writeByte(SWAP, pos + 1); - ca.setMaxStack(ca.getMaxStack() + 1); - } - - pos = iterator.next(); - } - - int mi = cp.addClassInfo(methodClassname); - String type = "(Ljava/lang/Object;" + typedesc + ")V"; - int methodref = cp.addMethodrefInfo(mi, methodName, type); - iterator.writeByte(INVOKESTATIC, pos); - iterator.write16bit(methodref, pos + 1); - } - } - - return pos; - } -} diff --git a/src/com/wenshuo/agent/javassist/convert/Transformer.java b/src/com/wenshuo/agent/javassist/convert/Transformer.java deleted file mode 100644 index 573dcf6..0000000 --- a/src/com/wenshuo/agent/javassist/convert/Transformer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.convert; - -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.CodeAttribute; -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; -import com.wenshuo.agent.javassist.bytecode.Opcode; - -/** - * Transformer and its subclasses are used for executing - * code transformation specified by CodeConverter. - * - * @see javassist.CodeConverter - */ -public abstract class Transformer implements Opcode { - private Transformer next; - - public Transformer(Transformer t) { - next = t; - } - - public Transformer getNext() { return next; } - - public void initialize(ConstPool cp, CodeAttribute attr) {} - - public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException { - initialize(cp, minfo.getCodeAttribute()); - } - - public void clean() {} - - public abstract int transform(CtClass clazz, int pos, CodeIterator it, - ConstPool cp) throws CannotCompileException, BadBytecode; - - public int extraLocals() { return 0; } - - public int extraStack() { return 0; } -} diff --git a/src/com/wenshuo/agent/javassist/expr/Cast.java b/src/com/wenshuo/agent/javassist/expr/Cast.java deleted file mode 100644 index 2f47ebf..0000000 --- a/src/com/wenshuo/agent/javassist/expr/Cast.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.*; -import com.wenshuo.agent.javassist.compiler.ast.ASTList; - -/** - * Explicit type cast. - */ -public class Cast extends Expr { - /** - * Undocumented constructor. Do not use; internal-use only. - */ - protected Cast(int pos, CodeIterator i, CtClass declaring, MethodInfo m) { - super(pos, i, declaring, m); - } - - /** - * Returns the method or constructor containing the type cast - * expression represented by this object. - */ - public CtBehavior where() { return super.where(); } - - /** - * Returns the line number of the source line containing the - * type-cast expression. - * - * @return -1 if this information is not available. - */ - public int getLineNumber() { - return super.getLineNumber(); - } - - /** - * Returns the source file containing the type-cast expression. - * - * @return null if this information is not available. - */ - public String getFileName() { - return super.getFileName(); - } - - /** - * Returns the CtClass object representing - * the type specified by the cast. - */ - public CtClass getType() throws NotFoundException { - ConstPool cp = getConstPool(); - int pos = currentPos; - int index = iterator.u16bitAt(pos + 1); - String name = cp.getClassInfo(index); - return thisClass.getClassPool().getCtClass(name); - } - - /** - * Returns the list of exceptions that the expression may throw. - * This list includes both the exceptions that the try-catch statements - * including the expression can catch and the exceptions that - * the throws declaration allows the method to throw. - */ - public CtClass[] mayThrow() { - return super.mayThrow(); - } - - /** - * Replaces the explicit cast operator with the bytecode derived from - * the given source text. - * - *

$0 is available but the value is null. - * - * @param statement a Java statement except try-catch. - */ - public void replace(String statement) throws CannotCompileException { - thisClass.getClassFile(); // to call checkModify(). - ConstPool constPool = getConstPool(); - int pos = currentPos; - int index = iterator.u16bitAt(pos + 1); - - Javac jc = new Javac(thisClass); - ClassPool cp = thisClass.getClassPool(); - CodeAttribute ca = iterator.get(); - - try { - CtClass[] params - = new CtClass[] { cp.get(javaLangObject) }; - CtClass retType = getType(); - - int paramVar = ca.getMaxLocals(); - jc.recordParams(javaLangObject, params, true, paramVar, - withinStatic()); - int retVar = jc.recordReturnType(retType, true); - jc.recordProceed(new ProceedForCast(index, retType)); - - /* Is $_ included in the source code? - */ - checkResultValue(retType, statement); - - Bytecode bytecode = jc.getBytecode(); - storeStack(params, true, paramVar, bytecode); - jc.recordLocalVariables(ca, pos); - - bytecode.addConstZero(retType); - bytecode.addStore(retVar, retType); // initialize $_ - - jc.compileStmnt(statement); - bytecode.addLoad(retVar, retType); - - replace0(pos, bytecode, 3); - } - catch (CompileError e) { throw new CannotCompileException(e); } - catch (NotFoundException e) { throw new CannotCompileException(e); } - catch (BadBytecode e) { - throw new CannotCompileException("broken method"); - } - } - - /* $proceed(Object obj) - */ - static class ProceedForCast implements ProceedHandler { - int index; - CtClass retType; - - ProceedForCast(int i, CtClass t) { - index = i; - retType = t; - } - - public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) - throws CompileError - { - if (gen.getMethodArgsLength(args) != 1) - throw new CompileError(Javac.proceedName - + "() cannot take more than one parameter " - + "for cast"); - - gen.atMethodArgs(args, new int[1], new int[1], new String[1]); - bytecode.addOpcode(Opcode.CHECKCAST); - bytecode.addIndex(index); - gen.setType(retType); - } - - public void setReturnType(JvstTypeChecker c, ASTList args) - throws CompileError - { - c.atMethodArgs(args, new int[1], new int[1], new String[1]); - c.setType(retType); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/expr/ConstructorCall.java b/src/com/wenshuo/agent/javassist/expr/ConstructorCall.java deleted file mode 100644 index b99bddc..0000000 --- a/src/com/wenshuo/agent/javassist/expr/ConstructorCall.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtConstructor; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; - -/** - * Constructor call such as this() and super() - * within a constructor body. - * - * @see NewExpr - */ -public class ConstructorCall extends MethodCall { - /** - * Undocumented constructor. Do not use; internal-use only. - */ - protected ConstructorCall(int pos, CodeIterator i, CtClass decl, MethodInfo m) { - super(pos, i, decl, m); - } - - /** - * Returns "super" or ""this". - */ - public String getMethodName() { - return isSuper() ? "super" : "this"; - } - - /** - * Always throws a NotFoundException. - * - * @see #getConstructor() - */ - public CtMethod getMethod() throws NotFoundException { - throw new NotFoundException("this is a constructor call. Call getConstructor()."); - } - - /** - * Returns the called constructor. - */ - public CtConstructor getConstructor() throws NotFoundException { - return getCtClass().getConstructor(getSignature()); - } - - /** - * Returns true if the called constructor is not this() - * but super() (a constructor declared in the super class). - */ - public boolean isSuper() { - return super.isSuper(); - } -} diff --git a/src/com/wenshuo/agent/javassist/expr/Expr.java b/src/com/wenshuo/agent/javassist/expr/Expr.java deleted file mode 100644 index 6dd56b9..0000000 --- a/src/com/wenshuo/agent/javassist/expr/Expr.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtBehavior; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtConstructor; -import com.wenshuo.agent.javassist.CtPrimitiveType; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.AccessFlag; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.Bytecode; -import com.wenshuo.agent.javassist.bytecode.ClassFile; -import com.wenshuo.agent.javassist.bytecode.CodeAttribute; -import com.wenshuo.agent.javassist.bytecode.CodeIterator; -import com.wenshuo.agent.javassist.bytecode.ConstPool; -import com.wenshuo.agent.javassist.bytecode.ExceptionTable; -import com.wenshuo.agent.javassist.bytecode.ExceptionsAttribute; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; -import com.wenshuo.agent.javassist.bytecode.Opcode; -import com.wenshuo.agent.javassist.compiler.Javac; - -import java.util.Iterator; -import java.util.LinkedList; - -/** - * Expression. - */ -public abstract class Expr implements Opcode { - int currentPos; - CodeIterator iterator; - CtClass thisClass; - MethodInfo thisMethod; - boolean edited; - int maxLocals, maxStack; - - static final String javaLangObject = "java.lang.Object"; - - /** - * Undocumented constructor. Do not use; internal-use only. - */ - protected Expr(int pos, CodeIterator i, CtClass declaring, MethodInfo m) { - currentPos = pos; - iterator = i; - thisClass = declaring; - thisMethod = m; - } - - /** - * Returns the class that declares the method enclosing - * this expression. - * - * @since 3.7 - */ - public CtClass getEnclosingClass() { return thisClass; } - - protected final ConstPool getConstPool() { - return thisMethod.getConstPool(); - } - - protected final boolean edited() { - return edited; - } - - protected final int locals() { - return maxLocals; - } - - protected final int stack() { - return maxStack; - } - - /** - * Returns true if this method is static. - */ - protected final boolean withinStatic() { - return (thisMethod.getAccessFlags() & AccessFlag.STATIC) != 0; - } - - /** - * Returns the constructor or method containing the expression. - */ - public CtBehavior where() { - MethodInfo mi = thisMethod; - CtBehavior[] cb = thisClass.getDeclaredBehaviors(); - for (int i = cb.length - 1; i >= 0; --i) - if (cb[i].getMethodInfo2() == mi) - return cb[i]; - - CtConstructor init = thisClass.getClassInitializer(); - if (init != null && init.getMethodInfo2() == mi) - return init; - - /* getDeclaredBehaviors() returns a list of methods/constructors. - * Although the list is cached in a CtClass object, it might be - * recreated for some reason. Thus, the member name and the signature - * must be also checked. - */ - for (int i = cb.length - 1; i >= 0; --i) { - if (thisMethod.getName().equals(cb[i].getMethodInfo2().getName()) - && thisMethod.getDescriptor() - .equals(cb[i].getMethodInfo2().getDescriptor())) { - return cb[i]; - } - } - - throw new RuntimeException("fatal: not found"); - } - - /** - * Returns the list of exceptions that the expression may throw. This list - * includes both the exceptions that the try-catch statements including the - * expression can catch and the exceptions that the throws declaration - * allows the method to throw. - */ - public CtClass[] mayThrow() { - ClassPool pool = thisClass.getClassPool(); - ConstPool cp = thisMethod.getConstPool(); - LinkedList list = new LinkedList(); - try { - CodeAttribute ca = thisMethod.getCodeAttribute(); - ExceptionTable et = ca.getExceptionTable(); - int pos = currentPos; - int n = et.size(); - for (int i = 0; i < n; ++i) - if (et.startPc(i) <= pos && pos < et.endPc(i)) { - int t = et.catchType(i); - if (t > 0) - try { - addClass(list, pool.get(cp.getClassInfo(t))); - } - catch (NotFoundException e) { - } - } - } - catch (NullPointerException e) { - } - - ExceptionsAttribute ea = thisMethod.getExceptionsAttribute(); - if (ea != null) { - String[] exceptions = ea.getExceptions(); - if (exceptions != null) { - int n = exceptions.length; - for (int i = 0; i < n; ++i) - try { - addClass(list, pool.get(exceptions[i])); - } - catch (NotFoundException e) { - } - } - } - - return (CtClass[])list.toArray(new CtClass[list.size()]); - } - - private static void addClass(LinkedList list, CtClass c) { - Iterator it = list.iterator(); - while (it.hasNext()) - if (it.next() == c) - return; - - list.add(c); - } - - /** - * Returns the index of the bytecode corresponding to the expression. It is - * the index into the byte array containing the Java bytecode that - * implements the method. - */ - public int indexOfBytecode() { - return currentPos; - } - - /** - * Returns the line number of the source line containing the expression. - * - * @return -1 if this information is not available. - */ - public int getLineNumber() { - return thisMethod.getLineNumber(currentPos); - } - - /** - * Returns the source file containing the expression. - * - * @return null if this information is not available. - */ - public String getFileName() { - ClassFile cf = thisClass.getClassFile2(); - if (cf == null) - return null; - else - return cf.getSourceFile(); - } - - static final boolean checkResultValue(CtClass retType, String prog) - throws CannotCompileException { - /* - * Is $_ included in the source code? - */ - boolean hasIt = (prog.indexOf(Javac.resultVarName) >= 0); - if (!hasIt && retType != CtClass.voidType) - throw new CannotCompileException( - "the resulting value is not stored in " - + Javac.resultVarName); - - return hasIt; - } - - /* - * If isStaticCall is true, null is assigned to $0. So $0 must be declared - * by calling Javac.recordParams(). - * - * After executing this method, the current stack depth might be less than - * 0. - */ - static final void storeStack(CtClass[] params, boolean isStaticCall, - int regno, Bytecode bytecode) { - storeStack0(0, params.length, params, regno + 1, bytecode); - if (isStaticCall) - bytecode.addOpcode(ACONST_NULL); - - bytecode.addAstore(regno); - } - - private static void storeStack0(int i, int n, CtClass[] params, int regno, - Bytecode bytecode) { - if (i >= n) - return; - else { - CtClass c = params[i]; - int size; - if (c instanceof CtPrimitiveType) - size = ((CtPrimitiveType)c).getDataSize(); - else - size = 1; - - storeStack0(i + 1, n, params, regno + size, bytecode); - bytecode.addStore(regno, c); - } - } - - // The implementation of replace() should call thisClass.checkModify() - // so that isModify() will return true. Otherwise, thisClass.classfile - // might be released during compilation and the compiler might generate - // bytecode with a wrong copy of ConstPool. - - /** - * Replaces this expression with the bytecode derived from - * the given source text. - * - * @param statement a Java statement except try-catch. - */ - public abstract void replace(String statement) throws CannotCompileException; - - /** - * Replaces this expression with the bytecode derived from - * the given source text and ExprEditor. - * - * @param statement a Java statement except try-catch. - * @param recursive if not null, the substituted bytecode - * is recursively processed by the given - * ExprEditor. - * @since 3.1 - */ - public void replace(String statement, ExprEditor recursive) - throws CannotCompileException - { - replace(statement); - if (recursive != null) - runEditor(recursive, iterator); - } - - protected void replace0(int pos, Bytecode bytecode, int size) - throws BadBytecode { - byte[] code = bytecode.get(); - edited = true; - int gap = code.length - size; - for (int i = 0; i < size; ++i) - iterator.writeByte(NOP, pos + i); - - if (gap > 0) - pos = iterator.insertGapAt(pos, gap, false).position; - - iterator.write(code, pos); - iterator.insert(bytecode.getExceptionTable(), pos); - maxLocals = bytecode.getMaxLocals(); - maxStack = bytecode.getMaxStack(); - } - - protected void runEditor(ExprEditor ed, CodeIterator oldIterator) - throws CannotCompileException - { - CodeAttribute codeAttr = oldIterator.get(); - int orgLocals = codeAttr.getMaxLocals(); - int orgStack = codeAttr.getMaxStack(); - int newLocals = locals(); - codeAttr.setMaxStack(stack()); - codeAttr.setMaxLocals(newLocals); - ExprEditor.LoopContext context - = new ExprEditor.LoopContext(newLocals); - int size = oldIterator.getCodeLength(); - int endPos = oldIterator.lookAhead(); - oldIterator.move(currentPos); - if (ed.doit(thisClass, thisMethod, context, oldIterator, endPos)) - edited = true; - - oldIterator.move(endPos + oldIterator.getCodeLength() - size); - codeAttr.setMaxLocals(orgLocals); - codeAttr.setMaxStack(orgStack); - maxLocals = context.maxLocals; - maxStack += context.maxStack; - } -} diff --git a/src/com/wenshuo/agent/javassist/expr/ExprEditor.java b/src/com/wenshuo/agent/javassist/expr/ExprEditor.java deleted file mode 100644 index c4711bd..0000000 --- a/src/com/wenshuo/agent/javassist/expr/ExprEditor.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CannotCompileException; - -/** - * A translator of method bodies. - * - *

The users can define a subclass of this class to customize how to - * modify a method body. The overall architecture is similar to the - * strategy pattern. - * - *

If instrument() is called in - * CtMethod, the method body is scanned from the beginning - * to the end. - * Whenever an expression, such as a method call and a new - * expression (object creation), - * is found, edit() is called in ExprEdit. - * edit() can inspect and modify the given expression. - * The modification is reflected on the original method body. If - * edit() does nothing, the original method body is not - * changed. - * - *

The following code is an example: - * - *

- * CtMethod cm = ...;
- * cm.instrument(new ExprEditor() {
- *     public void edit(MethodCall m) throws CannotCompileException {
- *         if (m.getClassName().equals("Point")) {
- *             System.out.println(m.getMethodName() + " line: "
- *                                + m.getLineNumber());
- *     }
- * });
- * 
- * - *

This code inspects all method calls appearing in the method represented - * by cm and it prints the names and the line numbers of the - * methods declared in class Point. This code does not modify - * the body of the method represented by cm. If the method - * body must be modified, call replace() - * in MethodCall. - * - * @see javassist.CtClass#instrument(ExprEditor) - * @see javassist.CtMethod#instrument(ExprEditor) - * @see javassist.CtConstructor#instrument(ExprEditor) - * @see MethodCall - * @see NewExpr - * @see FieldAccess - * - * @see javassist.CodeConverter - */ -public class ExprEditor { - /** - * Default constructor. It does nothing. - */ - public ExprEditor() {} - - /** - * Undocumented method. Do not use; internal-use only. - */ - public boolean doit(CtClass clazz, MethodInfo minfo) - throws CannotCompileException - { - CodeAttribute codeAttr = minfo.getCodeAttribute(); - if (codeAttr == null) - return false; - - CodeIterator iterator = codeAttr.iterator(); - boolean edited = false; - LoopContext context = new LoopContext(codeAttr.getMaxLocals()); - - while (iterator.hasNext()) - if (loopBody(iterator, clazz, minfo, context)) - edited = true; - - ExceptionTable et = codeAttr.getExceptionTable(); - int n = et.size(); - for (int i = 0; i < n; ++i) { - Handler h = new Handler(et, i, iterator, clazz, minfo); - edit(h); - if (h.edited()) { - edited = true; - context.updateMax(h.locals(), h.stack()); - } - } - - // codeAttr might be modified by other partiess - // so I check the current value of max-locals. - if (codeAttr.getMaxLocals() < context.maxLocals) - codeAttr.setMaxLocals(context.maxLocals); - - codeAttr.setMaxStack(codeAttr.getMaxStack() + context.maxStack); - try { - if (edited) - minfo.rebuildStackMapIf6(clazz.getClassPool(), - clazz.getClassFile2()); - } - catch (BadBytecode b) { - throw new CannotCompileException(b.getMessage(), b); - } - - return edited; - } - - /** - * Visits each bytecode in the given range. - */ - boolean doit(CtClass clazz, MethodInfo minfo, LoopContext context, - CodeIterator iterator, int endPos) - throws CannotCompileException - { - boolean edited = false; - while (iterator.hasNext() && iterator.lookAhead() < endPos) { - int size = iterator.getCodeLength(); - if (loopBody(iterator, clazz, minfo, context)) { - edited = true; - int size2 = iterator.getCodeLength(); - if (size != size2) // the body was modified. - endPos += size2 - size; - } - } - - return edited; - } - - final static class NewOp { - NewOp next; - int pos; - String type; - - NewOp(NewOp n, int p, String t) { - next = n; - pos = p; - type = t; - } - } - - final static class LoopContext { - NewOp newList; - int maxLocals; - int maxStack; - - LoopContext(int locals) { - maxLocals = locals; - maxStack = 0; - newList = null; - } - - void updateMax(int locals, int stack) { - if (maxLocals < locals) - maxLocals = locals; - - if (maxStack < stack) - maxStack = stack; - } - } - - final boolean loopBody(CodeIterator iterator, CtClass clazz, - MethodInfo minfo, LoopContext context) - throws CannotCompileException - { - try { - Expr expr = null; - int pos = iterator.next(); - int c = iterator.byteAt(pos); - - if (c < Opcode.GETSTATIC) // c < 178 - /* skip */; - else if (c < Opcode.NEWARRAY) { // c < 188 - if (c == Opcode.INVOKESTATIC - || c == Opcode.INVOKEINTERFACE - || c == Opcode.INVOKEVIRTUAL) { - expr = new MethodCall(pos, iterator, clazz, minfo); - edit((MethodCall)expr); - } - else if (c == Opcode.GETFIELD || c == Opcode.GETSTATIC - || c == Opcode.PUTFIELD - || c == Opcode.PUTSTATIC) { - expr = new FieldAccess(pos, iterator, clazz, minfo, c); - edit((FieldAccess)expr); - } - else if (c == Opcode.NEW) { - int index = iterator.u16bitAt(pos + 1); - context.newList = new NewOp(context.newList, pos, - minfo.getConstPool().getClassInfo(index)); - } - else if (c == Opcode.INVOKESPECIAL) { - NewOp newList = context.newList; - if (newList != null - && minfo.getConstPool().isConstructor(newList.type, - iterator.u16bitAt(pos + 1)) > 0) { - expr = new NewExpr(pos, iterator, clazz, minfo, - newList.type, newList.pos); - edit((NewExpr)expr); - context.newList = newList.next; - } - else { - MethodCall mcall = new MethodCall(pos, iterator, clazz, minfo); - if (mcall.getMethodName().equals(MethodInfo.nameInit)) { - ConstructorCall ccall = new ConstructorCall(pos, iterator, clazz, minfo); - expr = ccall; - edit(ccall); - } - else { - expr = mcall; - edit(mcall); - } - } - } - } - else { // c >= 188 - if (c == Opcode.NEWARRAY || c == Opcode.ANEWARRAY - || c == Opcode.MULTIANEWARRAY) { - expr = new NewArray(pos, iterator, clazz, minfo, c); - edit((NewArray)expr); - } - else if (c == Opcode.INSTANCEOF) { - expr = new Instanceof(pos, iterator, clazz, minfo); - edit((Instanceof)expr); - } - else if (c == Opcode.CHECKCAST) { - expr = new Cast(pos, iterator, clazz, minfo); - edit((Cast)expr); - } - } - - if (expr != null && expr.edited()) { - context.updateMax(expr.locals(), expr.stack()); - return true; - } - else - return false; - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - /** - * Edits a new expression (overridable). - * The default implementation performs nothing. - * - * @param e the new expression creating an object. - */ - public void edit(NewExpr e) throws CannotCompileException {} - - /** - * Edits an expression for array creation (overridable). - * The default implementation performs nothing. - * - * @param a the new expression for creating an array. - * @throws CannotCompileException - */ - public void edit(NewArray a) throws CannotCompileException {} - - /** - * Edits a method call (overridable). - * - * The default implementation performs nothing. - */ - public void edit(MethodCall m) throws CannotCompileException {} - - /** - * Edits a constructor call (overridable). - * The constructor call is either - * super() or this() - * included in a constructor body. - * - * The default implementation performs nothing. - * - * @see #edit(NewExpr) - */ - public void edit(ConstructorCall c) throws CannotCompileException {} - - /** - * Edits a field-access expression (overridable). - * Field access means both read and write. - * The default implementation performs nothing. - */ - public void edit(FieldAccess f) throws CannotCompileException {} - - /** - * Edits an instanceof expression (overridable). - * The default implementation performs nothing. - */ - public void edit(Instanceof i) throws CannotCompileException {} - - /** - * Edits an expression for explicit type casting (overridable). - * The default implementation performs nothing. - */ - public void edit(Cast c) throws CannotCompileException {} - - /** - * Edits a catch clause (overridable). - * The default implementation performs nothing. - */ - public void edit(Handler h) throws CannotCompileException {} -} diff --git a/src/com/wenshuo/agent/javassist/expr/FieldAccess.java b/src/com/wenshuo/agent/javassist/expr/FieldAccess.java deleted file mode 100644 index 523277e..0000000 --- a/src/com/wenshuo/agent/javassist/expr/FieldAccess.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.*; -import com.wenshuo.agent.javassist.compiler.ast.ASTList; - -/** - * Expression for accessing a field. - */ -public class FieldAccess extends Expr { - int opcode; - - protected FieldAccess(int pos, CodeIterator i, CtClass declaring, - MethodInfo m, int op) { - super(pos, i, declaring, m); - opcode = op; - } - - /** - * Returns the method or constructor containing the field-access - * expression represented by this object. - */ - public CtBehavior where() { return super.where(); } - - /** - * Returns the line number of the source line containing the - * field access. - * - * @return -1 if this information is not available. - */ - public int getLineNumber() { - return super.getLineNumber(); - } - - /** - * Returns the source file containing the field access. - * - * @return null if this information is not available. - */ - public String getFileName() { - return super.getFileName(); - } - - /** - * Returns true if the field is static. - */ - public boolean isStatic() { - return isStatic(opcode); - } - - static boolean isStatic(int c) { - return c == Opcode.GETSTATIC || c == Opcode.PUTSTATIC; - } - - /** - * Returns true if the field is read. - */ - public boolean isReader() { - return opcode == Opcode.GETFIELD || opcode == Opcode.GETSTATIC; - } - - /** - * Returns true if the field is written in. - */ - public boolean isWriter() { - return opcode == Opcode.PUTFIELD || opcode == Opcode.PUTSTATIC; - } - - /** - * Returns the class in which the field is declared. - */ - private CtClass getCtClass() throws NotFoundException { - return thisClass.getClassPool().get(getClassName()); - } - - /** - * Returns the name of the class in which the field is declared. - */ - public String getClassName() { - int index = iterator.u16bitAt(currentPos + 1); - return getConstPool().getFieldrefClassName(index); - } - - /** - * Returns the name of the field. - */ - public String getFieldName() { - int index = iterator.u16bitAt(currentPos + 1); - return getConstPool().getFieldrefName(index); - } - - /** - * Returns the field accessed by this expression. - */ - public CtField getField() throws NotFoundException { - CtClass cc = getCtClass(); - int index = iterator.u16bitAt(currentPos + 1); - ConstPool cp = getConstPool(); - return cc.getField(cp.getFieldrefName(index), cp.getFieldrefType(index)); - } - - /** - * Returns the list of exceptions that the expression may throw. - * This list includes both the exceptions that the try-catch statements - * including the expression can catch and the exceptions that - * the throws declaration allows the method to throw. - */ - public CtClass[] mayThrow() { - return super.mayThrow(); - } - - /** - * Returns the signature of the field type. - * The signature is represented by a character string - * called field descriptor, which is defined in the JVM specification. - * - * @see javassist.bytecode.Descriptor#toCtClass(String, ClassPool) - * @since 3.1 - */ - public String getSignature() { - int index = iterator.u16bitAt(currentPos + 1); - return getConstPool().getFieldrefType(index); - } - - /** - * Replaces the method call with the bytecode derived from - * the given source text. - * - *

$0 is available even if the called method is static. - * If the field access is writing, $_ is available but the value - * of $_ is ignored. - * - * @param statement a Java statement except try-catch. - */ - public void replace(String statement) throws CannotCompileException { - thisClass.getClassFile(); // to call checkModify(). - ConstPool constPool = getConstPool(); - int pos = currentPos; - int index = iterator.u16bitAt(pos + 1); - - Javac jc = new Javac(thisClass); - CodeAttribute ca = iterator.get(); - try { - CtClass[] params; - CtClass retType; - CtClass fieldType - = Descriptor.toCtClass(constPool.getFieldrefType(index), - thisClass.getClassPool()); - boolean read = isReader(); - if (read) { - params = new CtClass[0]; - retType = fieldType; - } - else { - params = new CtClass[1]; - params[0] = fieldType; - retType = CtClass.voidType; - } - - int paramVar = ca.getMaxLocals(); - jc.recordParams(constPool.getFieldrefClassName(index), params, - true, paramVar, withinStatic()); - - /* Is $_ included in the source code? - */ - boolean included = checkResultValue(retType, statement); - if (read) - included = true; - - int retVar = jc.recordReturnType(retType, included); - if (read) - jc.recordProceed(new ProceedForRead(retType, opcode, - index, paramVar)); - else { - // because $type is not the return type... - jc.recordType(fieldType); - jc.recordProceed(new ProceedForWrite(params[0], opcode, - index, paramVar)); - } - - Bytecode bytecode = jc.getBytecode(); - storeStack(params, isStatic(), paramVar, bytecode); - jc.recordLocalVariables(ca, pos); - - if (included) - if (retType == CtClass.voidType) { - bytecode.addOpcode(ACONST_NULL); - bytecode.addAstore(retVar); - } - else { - bytecode.addConstZero(retType); - bytecode.addStore(retVar, retType); // initialize $_ - } - - jc.compileStmnt(statement); - if (read) - bytecode.addLoad(retVar, retType); - - replace0(pos, bytecode, 3); - } - catch (CompileError e) { throw new CannotCompileException(e); } - catch (NotFoundException e) { throw new CannotCompileException(e); } - catch (BadBytecode e) { - throw new CannotCompileException("broken method"); - } - } - - /* $proceed() - */ - static class ProceedForRead implements ProceedHandler { - CtClass fieldType; - int opcode; - int targetVar, index; - - ProceedForRead(CtClass type, int op, int i, int var) { - fieldType = type; - targetVar = var; - opcode = op; - index = i; - } - - public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) - throws CompileError - { - if (args != null && !gen.isParamListName(args)) - throw new CompileError(Javac.proceedName - + "() cannot take a parameter for field reading"); - - int stack; - if (isStatic(opcode)) - stack = 0; - else { - stack = -1; - bytecode.addAload(targetVar); - } - - if (fieldType instanceof CtPrimitiveType) - stack += ((CtPrimitiveType)fieldType).getDataSize(); - else - ++stack; - - bytecode.add(opcode); - bytecode.addIndex(index); - bytecode.growStack(stack); - gen.setType(fieldType); - } - - public void setReturnType(JvstTypeChecker c, ASTList args) - throws CompileError - { - c.setType(fieldType); - } - } - - /* void $proceed() - * the return type is not the field type but void. - */ - static class ProceedForWrite implements ProceedHandler { - CtClass fieldType; - int opcode; - int targetVar, index; - - ProceedForWrite(CtClass type, int op, int i, int var) { - fieldType = type; - targetVar = var; - opcode = op; - index = i; - } - - public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) - throws CompileError - { - if (gen.getMethodArgsLength(args) != 1) - throw new CompileError(Javac.proceedName - + "() cannot take more than one parameter " - + "for field writing"); - - int stack; - if (isStatic(opcode)) - stack = 0; - else { - stack = -1; - bytecode.addAload(targetVar); - } - - gen.atMethodArgs(args, new int[1], new int[1], new String[1]); - gen.doNumCast(fieldType); - if (fieldType instanceof CtPrimitiveType) - stack -= ((CtPrimitiveType)fieldType).getDataSize(); - else - --stack; - - bytecode.add(opcode); - bytecode.addIndex(index); - bytecode.growStack(stack); - gen.setType(CtClass.voidType); - gen.addNullIfVoid(); - } - - public void setReturnType(JvstTypeChecker c, ASTList args) - throws CompileError - { - c.atMethodArgs(args, new int[1], new int[1], new String[1]); - c.setType(CtClass.voidType); - c.addNullIfVoid(); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/expr/Handler.java b/src/com/wenshuo/agent/javassist/expr/Handler.java deleted file mode 100644 index 66665c3..0000000 --- a/src/com/wenshuo/agent/javassist/expr/Handler.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.*; - -/** - * A catch clause or a finally block. - */ -public class Handler extends Expr { - private static String EXCEPTION_NAME = "$1"; - private ExceptionTable etable; - private int index; - - /** - * Undocumented constructor. Do not use; internal-use only. - */ - protected Handler(ExceptionTable et, int nth, - CodeIterator it, CtClass declaring, MethodInfo m) { - super(et.handlerPc(nth), it, declaring, m); - etable = et; - index = nth; - } - - /** - * Returns the method or constructor containing the catch clause. - */ - public CtBehavior where() { return super.where(); } - - /** - * Returns the source line number of the catch clause. - * - * @return -1 if this information is not available. - */ - public int getLineNumber() { - return super.getLineNumber(); - } - - /** - * Returns the source file containing the catch clause. - * - * @return null if this information is not available. - */ - public String getFileName() { - return super.getFileName(); - } - - /** - * Returns the list of exceptions that the catch clause may throw. - */ - public CtClass[] mayThrow() { - return super.mayThrow(); - } - - /** - * Returns the type handled by the catch clause. - * If this is a finally block, null is returned. - */ - public CtClass getType() throws NotFoundException { - int type = etable.catchType(index); - if (type == 0) - return null; - else { - ConstPool cp = getConstPool(); - String name = cp.getClassInfo(type); - return thisClass.getClassPool().getCtClass(name); - } - } - - /** - * Returns true if this is a finally block. - */ - public boolean isFinally() { - return etable.catchType(index) == 0; - } - - /** - * This method has not been implemented yet. - * - * @param statement a Java statement except try-catch. - */ - public void replace(String statement) throws CannotCompileException { - throw new RuntimeException("not implemented yet"); - } - - /** - * Inserts bytecode at the beginning of the catch clause. - * The caught exception is stored in $1. - * - * @param src the source code representing the inserted bytecode. - * It must be a single statement or block. - */ - public void insertBefore(String src) throws CannotCompileException { - edited = true; - - ConstPool cp = getConstPool(); - CodeAttribute ca = iterator.get(); - Javac jv = new Javac(thisClass); - Bytecode b = jv.getBytecode(); - b.setStackDepth(1); - b.setMaxLocals(ca.getMaxLocals()); - - try { - CtClass type = getType(); - int var = jv.recordVariable(type, EXCEPTION_NAME); - jv.recordReturnType(type, false); - b.addAstore(var); - jv.compileStmnt(src); - b.addAload(var); - - int oldHandler = etable.handlerPc(index); - b.addOpcode(Opcode.GOTO); - b.addIndex(oldHandler - iterator.getCodeLength() - - b.currentPc() + 1); - - maxStack = b.getMaxStack(); - maxLocals = b.getMaxLocals(); - - int pos = iterator.append(b.get()); - iterator.append(b.getExceptionTable(), pos); - etable.setHandlerPc(index, pos); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (CompileError e) { - throw new CannotCompileException(e); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/expr/Instanceof.java b/src/com/wenshuo/agent/javassist/expr/Instanceof.java deleted file mode 100644 index 1ea4ee1..0000000 --- a/src/com/wenshuo/agent/javassist/expr/Instanceof.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.*; -import com.wenshuo.agent.javassist.compiler.ast.ASTList; - -/** - * Instanceof operator. - */ -public class Instanceof extends Expr { - /** - * Undocumented constructor. Do not use; internal-use only. - */ - protected Instanceof(int pos, CodeIterator i, CtClass declaring, - MethodInfo m) { - super(pos, i, declaring, m); - } - - /** - * Returns the method or constructor containing the instanceof - * expression represented by this object. - */ - public CtBehavior where() { return super.where(); } - - /** - * Returns the line number of the source line containing the - * instanceof expression. - * - * @return -1 if this information is not available. - */ - public int getLineNumber() { - return super.getLineNumber(); - } - - /** - * Returns the source file containing the - * instanceof expression. - * - * @return null if this information is not available. - */ - public String getFileName() { - return super.getFileName(); - } - - /** - * Returns the CtClass object representing - * the type name on the right hand side - * of the instanceof operator. - */ - public CtClass getType() throws NotFoundException { - ConstPool cp = getConstPool(); - int pos = currentPos; - int index = iterator.u16bitAt(pos + 1); - String name = cp.getClassInfo(index); - return thisClass.getClassPool().getCtClass(name); - } - - /** - * Returns the list of exceptions that the expression may throw. - * This list includes both the exceptions that the try-catch statements - * including the expression can catch and the exceptions that - * the throws declaration allows the method to throw. - */ - public CtClass[] mayThrow() { - return super.mayThrow(); - } - - /** - * Replaces the instanceof operator with the bytecode derived from - * the given source text. - * - *

$0 is available but the value is null. - * - * @param statement a Java statement except try-catch. - */ - public void replace(String statement) throws CannotCompileException { - thisClass.getClassFile(); // to call checkModify(). - ConstPool constPool = getConstPool(); - int pos = currentPos; - int index = iterator.u16bitAt(pos + 1); - - Javac jc = new Javac(thisClass); - ClassPool cp = thisClass.getClassPool(); - CodeAttribute ca = iterator.get(); - - try { - CtClass[] params - = new CtClass[] { cp.get(javaLangObject) }; - CtClass retType = CtClass.booleanType; - - int paramVar = ca.getMaxLocals(); - jc.recordParams(javaLangObject, params, true, paramVar, - withinStatic()); - int retVar = jc.recordReturnType(retType, true); - jc.recordProceed(new ProceedForInstanceof(index)); - - // because $type is not the return type... - jc.recordType(getType()); - - /* Is $_ included in the source code? - */ - checkResultValue(retType, statement); - - Bytecode bytecode = jc.getBytecode(); - storeStack(params, true, paramVar, bytecode); - jc.recordLocalVariables(ca, pos); - - bytecode.addConstZero(retType); - bytecode.addStore(retVar, retType); // initialize $_ - - jc.compileStmnt(statement); - bytecode.addLoad(retVar, retType); - - replace0(pos, bytecode, 3); - } - catch (CompileError e) { throw new CannotCompileException(e); } - catch (NotFoundException e) { throw new CannotCompileException(e); } - catch (BadBytecode e) { - throw new CannotCompileException("broken method"); - } - } - - /* boolean $proceed(Object obj) - */ - static class ProceedForInstanceof implements ProceedHandler { - int index; - - ProceedForInstanceof(int i) { - index = i; - } - - public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) - throws CompileError - { - if (gen.getMethodArgsLength(args) != 1) - throw new CompileError(Javac.proceedName - + "() cannot take more than one parameter " - + "for instanceof"); - - gen.atMethodArgs(args, new int[1], new int[1], new String[1]); - bytecode.addOpcode(Opcode.INSTANCEOF); - bytecode.addIndex(index); - gen.setType(CtClass.booleanType); - } - - public void setReturnType(JvstTypeChecker c, ASTList args) - throws CompileError - { - c.atMethodArgs(args, new int[1], new int[1], new String[1]); - c.setType(CtClass.booleanType); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/expr/MethodCall.java b/src/com/wenshuo/agent/javassist/expr/MethodCall.java deleted file mode 100644 index 70496b1..0000000 --- a/src/com/wenshuo/agent/javassist/expr/MethodCall.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.*; - -/** - * Method invocation (caller-side expression). - */ -public class MethodCall extends Expr { - /** - * Undocumented constructor. Do not use; internal-use only. - */ - protected MethodCall(int pos, CodeIterator i, CtClass declaring, - MethodInfo m) { - super(pos, i, declaring, m); - } - - private int getNameAndType(ConstPool cp) { - int pos = currentPos; - int c = iterator.byteAt(pos); - int index = iterator.u16bitAt(pos + 1); - - if (c == INVOKEINTERFACE) - return cp.getInterfaceMethodrefNameAndType(index); - else - return cp.getMethodrefNameAndType(index); - } - - /** - * Returns the method or constructor containing the method-call - * expression represented by this object. - */ - public CtBehavior where() { return super.where(); } - - /** - * Returns the line number of the source line containing the - * method call. - * - * @return -1 if this information is not available. - */ - public int getLineNumber() { - return super.getLineNumber(); - } - - /** - * Returns the source file containing the method call. - * - * @return null if this information is not available. - */ - public String getFileName() { - return super.getFileName(); - } - - /** - * Returns the class of the target object, - * which the method is called on. - */ - protected CtClass getCtClass() throws NotFoundException { - return thisClass.getClassPool().get(getClassName()); - } - - /** - * Returns the class name of the target object, - * which the method is called on. - */ - public String getClassName() { - String cname; - - ConstPool cp = getConstPool(); - int pos = currentPos; - int c = iterator.byteAt(pos); - int index = iterator.u16bitAt(pos + 1); - - if (c == INVOKEINTERFACE) - cname = cp.getInterfaceMethodrefClassName(index); - else - cname = cp.getMethodrefClassName(index); - - if (cname.charAt(0) == '[') - cname = Descriptor.toClassName(cname); - - return cname; - } - - /** - * Returns the name of the called method. - */ - public String getMethodName() { - ConstPool cp = getConstPool(); - int nt = getNameAndType(cp); - return cp.getUtf8Info(cp.getNameAndTypeName(nt)); - } - - /** - * Returns the called method. - */ - public CtMethod getMethod() throws NotFoundException { - return getCtClass().getMethod(getMethodName(), getSignature()); - } - - /** - * Returns the method signature (the parameter types - * and the return type). - * The method signature is represented by a character string - * called method descriptor, which is defined in the JVM specification. - * - * @see javassist.CtBehavior#getSignature() - * @see javassist.bytecode.Descriptor - * @since 3.1 - */ - public String getSignature() { - ConstPool cp = getConstPool(); - int nt = getNameAndType(cp); - return cp.getUtf8Info(cp.getNameAndTypeDescriptor(nt)); - } - - /** - * Returns the list of exceptions that the expression may throw. - * This list includes both the exceptions that the try-catch statements - * including the expression can catch and the exceptions that - * the throws declaration allows the method to throw. - */ - public CtClass[] mayThrow() { - return super.mayThrow(); - } - - /** - * Returns true if the called method is of a superclass of the current - * class. - */ - public boolean isSuper() { - return iterator.byteAt(currentPos) == INVOKESPECIAL - && !where().getDeclaringClass().getName().equals(getClassName()); - } - - /* - * Returns the parameter types of the called method. - - public CtClass[] getParameterTypes() throws NotFoundException { - return Descriptor.getParameterTypes(getMethodDesc(), - thisClass.getClassPool()); - } - */ - - /* - * Returns the return type of the called method. - - public CtClass getReturnType() throws NotFoundException { - return Descriptor.getReturnType(getMethodDesc(), - thisClass.getClassPool()); - } - */ - - /** - * Replaces the method call with the bytecode derived from - * the given source text. - * - *

$0 is available even if the called method is static. - * - * @param statement a Java statement except try-catch. - */ - public void replace(String statement) throws CannotCompileException { - thisClass.getClassFile(); // to call checkModify(). - ConstPool constPool = getConstPool(); - int pos = currentPos; - int index = iterator.u16bitAt(pos + 1); - - String classname, methodname, signature; - int opcodeSize; - int c = iterator.byteAt(pos); - if (c == INVOKEINTERFACE) { - opcodeSize = 5; - classname = constPool.getInterfaceMethodrefClassName(index); - methodname = constPool.getInterfaceMethodrefName(index); - signature = constPool.getInterfaceMethodrefType(index); - } - else if (c == INVOKESTATIC - || c == INVOKESPECIAL || c == INVOKEVIRTUAL) { - opcodeSize = 3; - classname = constPool.getMethodrefClassName(index); - methodname = constPool.getMethodrefName(index); - signature = constPool.getMethodrefType(index); - } - else - throw new CannotCompileException("not method invocation"); - - Javac jc = new Javac(thisClass); - ClassPool cp = thisClass.getClassPool(); - CodeAttribute ca = iterator.get(); - try { - CtClass[] params = Descriptor.getParameterTypes(signature, cp); - CtClass retType = Descriptor.getReturnType(signature, cp); - int paramVar = ca.getMaxLocals(); - jc.recordParams(classname, params, - true, paramVar, withinStatic()); - int retVar = jc.recordReturnType(retType, true); - if (c == INVOKESTATIC) - jc.recordStaticProceed(classname, methodname); - else if (c == INVOKESPECIAL) - jc.recordSpecialProceed(Javac.param0Name, classname, - methodname, signature); - else - jc.recordProceed(Javac.param0Name, methodname); - - /* Is $_ included in the source code? - */ - checkResultValue(retType, statement); - - Bytecode bytecode = jc.getBytecode(); - storeStack(params, c == INVOKESTATIC, paramVar, bytecode); - jc.recordLocalVariables(ca, pos); - - if (retType != CtClass.voidType) { - bytecode.addConstZero(retType); - bytecode.addStore(retVar, retType); // initialize $_ - } - - jc.compileStmnt(statement); - if (retType != CtClass.voidType) - bytecode.addLoad(retVar, retType); - - replace0(pos, bytecode, opcodeSize); - } - catch (CompileError e) { throw new CannotCompileException(e); } - catch (NotFoundException e) { throw new CannotCompileException(e); } - catch (BadBytecode e) { - throw new CannotCompileException("broken method"); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/expr/NewArray.java b/src/com/wenshuo/agent/javassist/expr/NewArray.java deleted file mode 100644 index 1c8f1e6..0000000 --- a/src/com/wenshuo/agent/javassist/expr/NewArray.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.*; -import com.wenshuo.agent.javassist.compiler.ast.ASTList; - -/** - * Array creation. - * - *

This class does not provide methods for obtaining the initial - * values of array elements. - */ -public class NewArray extends Expr { - int opcode; - - protected NewArray(int pos, CodeIterator i, CtClass declaring, - MethodInfo m, int op) { - super(pos, i, declaring, m); - opcode = op; - } - - /** - * Returns the method or constructor containing the array creation - * represented by this object. - */ - public CtBehavior where() { return super.where(); } - - /** - * Returns the line number of the source line containing the - * array creation. - * - * @return -1 if this information is not available. - */ - public int getLineNumber() { - return super.getLineNumber(); - } - - /** - * Returns the source file containing the array creation. - * - * @return null if this information is not available. - */ - public String getFileName() { - return super.getFileName(); - } - - /** - * Returns the list of exceptions that the expression may throw. - * This list includes both the exceptions that the try-catch statements - * including the expression can catch and the exceptions that - * the throws declaration allows the method to throw. - */ - public CtClass[] mayThrow() { - return super.mayThrow(); - } - - /** - * Returns the type of array components. If the created array is - * a two-dimensional array of int, - * the type returned by this method is - * not int[] but int. - */ - public CtClass getComponentType() throws NotFoundException { - if (opcode == Opcode.NEWARRAY) { - int atype = iterator.byteAt(currentPos + 1); - return getPrimitiveType(atype); - } - else if (opcode == Opcode.ANEWARRAY - || opcode == Opcode.MULTIANEWARRAY) { - int index = iterator.u16bitAt(currentPos + 1); - String desc = getConstPool().getClassInfo(index); - int dim = Descriptor.arrayDimension(desc); - desc = Descriptor.toArrayComponent(desc, dim); - return Descriptor.toCtClass(desc, thisClass.getClassPool()); - } - else - throw new RuntimeException("bad opcode: " + opcode); - } - - CtClass getPrimitiveType(int atype) { - switch (atype) { - case Opcode.T_BOOLEAN : - return CtClass.booleanType; - case Opcode.T_CHAR : - return CtClass.charType; - case Opcode.T_FLOAT : - return CtClass.floatType; - case Opcode.T_DOUBLE : - return CtClass.doubleType; - case Opcode.T_BYTE : - return CtClass.byteType; - case Opcode.T_SHORT : - return CtClass.shortType; - case Opcode.T_INT : - return CtClass.intType; - case Opcode.T_LONG : - return CtClass.longType; - default : - throw new RuntimeException("bad atype: " + atype); - } - } - - /** - * Returns the dimension of the created array. - */ - public int getDimension() { - if (opcode == Opcode.NEWARRAY) - return 1; - else if (opcode == Opcode.ANEWARRAY - || opcode == Opcode.MULTIANEWARRAY) { - int index = iterator.u16bitAt(currentPos + 1); - String desc = getConstPool().getClassInfo(index); - return Descriptor.arrayDimension(desc) - + (opcode == Opcode.ANEWARRAY ? 1 : 0); - } - else - throw new RuntimeException("bad opcode: " + opcode); - } - - /** - * Returns the number of dimensions of arrays to be created. - * If the opcode is multianewarray, this method returns the second - * operand. Otherwise, it returns 1. - */ - public int getCreatedDimensions() { - if (opcode == Opcode.MULTIANEWARRAY) - return iterator.byteAt(currentPos + 3); - else - return 1; - } - - /** - * Replaces the array creation with the bytecode derived from - * the given source text. - * - *

$0 is available even if the called method is static. - * If the field access is writing, $_ is available but the value - * of $_ is ignored. - * - * @param statement a Java statement except try-catch. - */ - public void replace(String statement) throws CannotCompileException { - try { - replace2(statement); - } - catch (CompileError e) { throw new CannotCompileException(e); } - catch (NotFoundException e) { throw new CannotCompileException(e); } - catch (BadBytecode e) { - throw new CannotCompileException("broken method"); - } - } - - private void replace2(String statement) - throws CompileError, NotFoundException, BadBytecode, - CannotCompileException - { - thisClass.getClassFile(); // to call checkModify(). - ConstPool constPool = getConstPool(); - int pos = currentPos; - CtClass retType; - int codeLength; - int index = 0; - int dim = 1; - String desc; - if (opcode == Opcode.NEWARRAY) { - index = iterator.byteAt(currentPos + 1); // atype - CtPrimitiveType cpt = (CtPrimitiveType)getPrimitiveType(index); - desc = "[" + cpt.getDescriptor(); - codeLength = 2; - } - else if (opcode == Opcode.ANEWARRAY) { - index = iterator.u16bitAt(pos + 1); - desc = constPool.getClassInfo(index); - if (desc.startsWith("[")) - desc = "[" + desc; - else - desc = "[L" + desc + ";"; - - codeLength = 3; - } - else if (opcode == Opcode.MULTIANEWARRAY) { - index = iterator.u16bitAt(currentPos + 1); - desc = constPool.getClassInfo(index); - dim = iterator.byteAt(currentPos + 3); - codeLength = 4; - } - else - throw new RuntimeException("bad opcode: " + opcode); - - retType = Descriptor.toCtClass(desc, thisClass.getClassPool()); - - Javac jc = new Javac(thisClass); - CodeAttribute ca = iterator.get(); - - CtClass[] params = new CtClass[dim]; - for (int i = 0; i < dim; ++i) - params[i] = CtClass.intType; - - int paramVar = ca.getMaxLocals(); - jc.recordParams(javaLangObject, params, - true, paramVar, withinStatic()); - - /* Is $_ included in the source code? - */ - checkResultValue(retType, statement); - int retVar = jc.recordReturnType(retType, true); - jc.recordProceed(new ProceedForArray(retType, opcode, index, dim)); - - Bytecode bytecode = jc.getBytecode(); - storeStack(params, true, paramVar, bytecode); - jc.recordLocalVariables(ca, pos); - - bytecode.addOpcode(ACONST_NULL); // initialize $_ - bytecode.addAstore(retVar); - - jc.compileStmnt(statement); - bytecode.addAload(retVar); - - replace0(pos, bytecode, codeLength); - } - - /* $proceed( ..) - */ - static class ProceedForArray implements ProceedHandler { - CtClass arrayType; - int opcode; - int index, dimension; - - ProceedForArray(CtClass type, int op, int i, int dim) { - arrayType = type; - opcode = op; - index = i; - dimension = dim; - } - - public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) - throws CompileError - { - int num = gen.getMethodArgsLength(args); - if (num != dimension) - throw new CompileError(Javac.proceedName - + "() with a wrong number of parameters"); - - gen.atMethodArgs(args, new int[num], - new int[num], new String[num]); - bytecode.addOpcode(opcode); - if (opcode == Opcode.ANEWARRAY) - bytecode.addIndex(index); - else if (opcode == Opcode.NEWARRAY) - bytecode.add(index); - else /* if (opcode == Opcode.MULTIANEWARRAY) */ { - bytecode.addIndex(index); - bytecode.add(dimension); - bytecode.growStack(1 - dimension); - } - - gen.setType(arrayType); - } - - public void setReturnType(JvstTypeChecker c, ASTList args) - throws CompileError - { - c.setType(arrayType); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/expr/NewExpr.java b/src/com/wenshuo/agent/javassist/expr/NewExpr.java deleted file mode 100644 index e3f1a14..0000000 --- a/src/com/wenshuo/agent/javassist/expr/NewExpr.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.expr; - -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.bytecode.*; -import com.wenshuo.agent.javassist.compiler.*; -import com.wenshuo.agent.javassist.compiler.ast.ASTList; - -/** - * Object creation (new expression). - */ -public class NewExpr extends Expr { - String newTypeName; - int newPos; - - /** - * Undocumented constructor. Do not use; internal-use only. - */ - protected NewExpr(int pos, CodeIterator i, CtClass declaring, - MethodInfo m, String type, int np) { - super(pos, i, declaring, m); - newTypeName = type; - newPos = np; - } - - /* - * Not used - * - private int getNameAndType(ConstPool cp) { - int pos = currentPos; - int c = iterator.byteAt(pos); - int index = iterator.u16bitAt(pos + 1); - - if (c == INVOKEINTERFACE) - return cp.getInterfaceMethodrefNameAndType(index); - else - return cp.getMethodrefNameAndType(index); - } */ - - /** - * Returns the method or constructor containing the new - * expression represented by this object. - */ - public CtBehavior where() { return super.where(); } - - /** - * Returns the line number of the source line containing the - * new expression. - * - * @return -1 if this information is not available. - */ - public int getLineNumber() { - return super.getLineNumber(); - } - - /** - * Returns the source file containing the new expression. - * - * @return null if this information is not available. - */ - public String getFileName() { - return super.getFileName(); - } - - /** - * Returns the class of the created object. - */ - private CtClass getCtClass() throws NotFoundException { - return thisClass.getClassPool().get(newTypeName); - } - - /** - * Returns the class name of the created object. - */ - public String getClassName() { - return newTypeName; - } - - /** - * Get the signature of the constructor - * - * The signature is represented by a character string - * called method descriptor, which is defined in the JVM specification. - * - * @see javassist.CtBehavior#getSignature() - * @see javassist.bytecode.Descriptor - * @return the signature - */ - public String getSignature() { - ConstPool constPool = getConstPool(); - int methodIndex = iterator.u16bitAt(currentPos + 1); // constructor - return constPool.getMethodrefType(methodIndex); - } - - /** - * Returns the constructor called for creating the object. - */ - public CtConstructor getConstructor() throws NotFoundException { - ConstPool cp = getConstPool(); - int index = iterator.u16bitAt(currentPos + 1); - String desc = cp.getMethodrefType(index); - return getCtClass().getConstructor(desc); - } - - /** - * Returns the list of exceptions that the expression may throw. - * This list includes both the exceptions that the try-catch statements - * including the expression can catch and the exceptions that - * the throws declaration allows the method to throw. - */ - public CtClass[] mayThrow() { - return super.mayThrow(); - } - - /* - * Returns the parameter types of the constructor. - - public CtClass[] getParameterTypes() throws NotFoundException { - ConstPool cp = getConstPool(); - int index = iterator.u16bitAt(currentPos + 1); - String desc = cp.getMethodrefType(index); - return Descriptor.getParameterTypes(desc, thisClass.getClassPool()); - } - */ - - private int canReplace() throws CannotCompileException { - int op = iterator.byteAt(newPos + 3); - if (op == Opcode.DUP) // Typical single DUP or Javaflow DUP DUP2_X2 POP2 - return ((iterator.byteAt(newPos + 4) == Opcode.DUP2_X2 - && iterator.byteAt(newPos + 5) == Opcode.POP2)) ? 6 : 4; - else if (op == Opcode.DUP_X1 - && iterator.byteAt(newPos + 4) == Opcode.SWAP) - return 5; - else - return 3; // for Eclipse. The generated code may include no DUP. - // throw new CannotCompileException( - // "sorry, cannot edit NEW followed by no DUP"); - } - - /** - * Replaces the new expression with the bytecode derived from - * the given source text. - * - *

$0 is available but the value is null. - * - * @param statement a Java statement except try-catch. - */ - public void replace(String statement) throws CannotCompileException { - thisClass.getClassFile(); // to call checkModify(). - - final int bytecodeSize = 3; - int pos = newPos; - - int newIndex = iterator.u16bitAt(pos + 1); - - /* delete the preceding NEW and DUP (or DUP_X1, SWAP) instructions. - */ - int codeSize = canReplace(); - int end = pos + codeSize; - for (int i = pos; i < end; ++i) - iterator.writeByte(NOP, i); - - ConstPool constPool = getConstPool(); - pos = currentPos; - int methodIndex = iterator.u16bitAt(pos + 1); // constructor - - String signature = constPool.getMethodrefType(methodIndex); - - Javac jc = new Javac(thisClass); - ClassPool cp = thisClass.getClassPool(); - CodeAttribute ca = iterator.get(); - try { - CtClass[] params = Descriptor.getParameterTypes(signature, cp); - CtClass newType = cp.get(newTypeName); - int paramVar = ca.getMaxLocals(); - jc.recordParams(newTypeName, params, - true, paramVar, withinStatic()); - int retVar = jc.recordReturnType(newType, true); - jc.recordProceed(new ProceedForNew(newType, newIndex, - methodIndex)); - - /* Is $_ included in the source code? - */ - checkResultValue(newType, statement); - - Bytecode bytecode = jc.getBytecode(); - storeStack(params, true, paramVar, bytecode); - jc.recordLocalVariables(ca, pos); - - bytecode.addConstZero(newType); - bytecode.addStore(retVar, newType); // initialize $_ - - jc.compileStmnt(statement); - if (codeSize > 3) // if the original code includes DUP. - bytecode.addAload(retVar); - - replace0(pos, bytecode, bytecodeSize); - } - catch (CompileError e) { throw new CannotCompileException(e); } - catch (NotFoundException e) { throw new CannotCompileException(e); } - catch (BadBytecode e) { - throw new CannotCompileException("broken method"); - } - } - - static class ProceedForNew implements ProceedHandler { - CtClass newType; - int newIndex, methodIndex; - - ProceedForNew(CtClass nt, int ni, int mi) { - newType = nt; - newIndex = ni; - methodIndex = mi; - } - - public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) - throws CompileError - { - bytecode.addOpcode(NEW); - bytecode.addIndex(newIndex); - bytecode.addOpcode(DUP); - gen.atMethodCallCore(newType, MethodInfo.nameInit, args, - false, true, -1, null); - gen.setType(newType); - } - - public void setReturnType(JvstTypeChecker c, ASTList args) - throws CompileError - { - c.atMethodCallCore(newType, MethodInfo.nameInit, args); - c.setType(newType); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/expr/package.html b/src/com/wenshuo/agent/javassist/expr/package.html deleted file mode 100644 index 12a2c63..0000000 --- a/src/com/wenshuo/agent/javassist/expr/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - -

This package contains the classes for modifying a method body. -See ExprEditor (expression editor) for more details. - - - diff --git a/src/com/wenshuo/agent/javassist/package.html b/src/com/wenshuo/agent/javassist/package.html deleted file mode 100644 index f5b66b9..0000000 --- a/src/com/wenshuo/agent/javassist/package.html +++ /dev/null @@ -1,22 +0,0 @@ - - -The Javassist Core API. - -

Javassist (Java programming assistant) makes bytecode -engineering simple. It is a class library for editing -bytecode in Java; it enables Java programs to define a new class at -runtime and to modify a given class file when the JVM loads it. - -

The most significant class of this package is CtClass. -See the description of this class first. - -

To know the version number of this package, type the following command: - -

    -java -jar javassist.jar
    -
- -

It prints the version number on the console. - - - diff --git a/src/com/wenshuo/agent/javassist/runtime/Cflow.java b/src/com/wenshuo/agent/javassist/runtime/Cflow.java deleted file mode 100644 index f2c7b6a..0000000 --- a/src/com/wenshuo/agent/javassist/runtime/Cflow.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.runtime; - -/** - * A support class for implementing $cflow. - * This support class is required at runtime - * only if $cflow is used. - * - * @see javassist.CtBehavior#useCflow(String) - */ -public class Cflow extends ThreadLocal { - private static class Depth { - private int depth; - Depth() { depth = 0; } - int get() { return depth; } - void inc() { ++depth; } - void dec() { --depth; } - } - - protected synchronized Object initialValue() { - return new Depth(); - } - - /** - * Increments the counter. - */ - public void enter() { ((Depth)get()).inc(); } - - /** - * Decrements the counter. - */ - public void exit() { ((Depth)get()).dec(); } - - /** - * Returns the value of the counter. - */ - public int value() { return ((Depth)get()).get(); } -} diff --git a/src/com/wenshuo/agent/javassist/runtime/Desc.java b/src/com/wenshuo/agent/javassist/runtime/Desc.java deleted file mode 100644 index e8ed8bf..0000000 --- a/src/com/wenshuo/agent/javassist/runtime/Desc.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.runtime; - -/** - * A support class for implementing $sig and - * $type. - * This support class is required at runtime - * only if $sig or $type is used. - */ -public class Desc { - - /** - * Specifies how a java.lang.Class object is loaded. - * - *

If true, it is loaded by: - *

Thread.currentThread().getContextClassLoader().loadClass()
- *

If false, it is loaded by Class.forName(). - * The default value is false. - */ - public static boolean useContextClassLoader = false; - - private static Class getClassObject(String name) - throws ClassNotFoundException - { - if (useContextClassLoader) - return Class.forName(name, true, Thread.currentThread().getContextClassLoader()); - else - return Class.forName(name); - } - - /** - * Interprets the given class name. - * It is used for implementing $class. - */ - public static Class getClazz(String name) { - try { - return getClassObject(name); - } - catch (ClassNotFoundException e) { - throw new RuntimeException( - "$class: internal error, could not find class '" + name - + "' (Desc.useContextClassLoader: " - + Boolean.toString(useContextClassLoader) + ")", e); - } - } - - /** - * Interprets the given type descriptor representing a method - * signature. It is used for implementing $sig. - */ - public static Class[] getParams(String desc) { - if (desc.charAt(0) != '(') - throw new RuntimeException("$sig: internal error"); - - return getType(desc, desc.length(), 1, 0); - } - - /** - * Interprets the given type descriptor. - * It is used for implementing $type. - */ - public static Class getType(String desc) { - Class[] result = getType(desc, desc.length(), 0, 0); - if (result == null || result.length != 1) - throw new RuntimeException("$type: internal error"); - - return result[0]; - } - - private static Class[] getType(String desc, int descLen, - int start, int num) { - Class clazz; - if (start >= descLen) - return new Class[num]; - - char c = desc.charAt(start); - switch (c) { - case 'Z' : - clazz = Boolean.TYPE; - break; - case 'C' : - clazz = Character.TYPE; - break; - case 'B' : - clazz = Byte.TYPE; - break; - case 'S' : - clazz = Short.TYPE; - break; - case 'I' : - clazz = Integer.TYPE; - break; - case 'J' : - clazz = Long.TYPE; - break; - case 'F' : - clazz = Float.TYPE; - break; - case 'D' : - clazz = Double.TYPE; - break; - case 'V' : - clazz = Void.TYPE; - break; - case 'L' : - case '[' : - return getClassType(desc, descLen, start, num); - default : - return new Class[num]; - } - - Class[] result = getType(desc, descLen, start + 1, num + 1); - result[num] = clazz; - return result; - } - - private static Class[] getClassType(String desc, int descLen, - int start, int num) { - int end = start; - while (desc.charAt(end) == '[') - ++end; - - if (desc.charAt(end) == 'L') { - end = desc.indexOf(';', end); - if (end < 0) - throw new IndexOutOfBoundsException("bad descriptor"); - } - - String cname; - if (desc.charAt(start) == 'L') - cname = desc.substring(start + 1, end); - else - cname = desc.substring(start, end + 1); - - Class[] result = getType(desc, descLen, end + 1, num + 1); - try { - result[num] = getClassObject(cname.replace('/', '.')); - } - catch (ClassNotFoundException e) { - // "new RuntimeException(e)" is not available in JDK 1.3. - throw new RuntimeException(e.getMessage()); - } - - return result; - } -} diff --git a/src/com/wenshuo/agent/javassist/runtime/DotClass.java b/src/com/wenshuo/agent/javassist/runtime/DotClass.java deleted file mode 100644 index e3adef4..0000000 --- a/src/com/wenshuo/agent/javassist/runtime/DotClass.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.runtime; - -/** - * A support class for implementing .class notation. - * This is required at runtime - * only if .class notation is used in source code given - * to the Javassist compiler. - */ -public class DotClass { - public static NoClassDefFoundError fail(ClassNotFoundException e) { - return new NoClassDefFoundError(e.getMessage()); - } -} diff --git a/src/com/wenshuo/agent/javassist/runtime/Inner.java b/src/com/wenshuo/agent/javassist/runtime/Inner.java deleted file mode 100644 index e51ab88..0000000 --- a/src/com/wenshuo/agent/javassist/runtime/Inner.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.runtime; - -/** - * A support class for compiling a method declared in an inner class. - * This support class is required at runtime - * only if the method calls a private constructor in the enclosing class. - */ -public class Inner { -} diff --git a/src/com/wenshuo/agent/javassist/runtime/package.html b/src/com/wenshuo/agent/javassist/runtime/package.html deleted file mode 100644 index 313648f..0000000 --- a/src/com/wenshuo/agent/javassist/runtime/package.html +++ /dev/null @@ -1,12 +0,0 @@ - - -Runtime support classes required by modified bytecode. - -

This package includes support classes that may be required by -classes modified with Javassist. Note that most of the modified -classes do not require these support classes. See the documentation -of every support class to know which kind of modification needs -a support class. - - - diff --git a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java b/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java deleted file mode 100644 index ce0dbd8..0000000 --- a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.scopedpool; - -import java.lang.ref.WeakReference; -import java.security.ProtectionDomain; -import java.util.Iterator; -import java.util.Map; -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.LoaderClassPath; -import com.wenshuo.agent.javassist.NotFoundException; - -/** - * A scoped class pool. - * - * @author Bill Burke - * @author Adrian Brock - * @author Kabir Khan - * @version $Revision: 1.8 $ - */ -public class ScopedClassPool extends ClassPool { - protected ScopedClassPoolRepository repository; - - protected WeakReference classLoader; - - protected LoaderClassPath classPath; - - protected SoftValueHashMap softcache = new SoftValueHashMap(); - - boolean isBootstrapCl = true; - - static { - ClassPool.doPruning = false; - ClassPool.releaseUnmodifiedClassFile = false; - } - - /** - * Create a new ScopedClassPool. - * - * @param cl - * the classloader - * @param src - * the original class pool - * @param repository - * the repository - *@deprecated - */ - protected ScopedClassPool(ClassLoader cl, ClassPool src, - ScopedClassPoolRepository repository) { - this(cl, src, repository, false); - } - - /** - * Create a new ScopedClassPool. - * - * @param cl - * the classloader - * @param src - * the original class pool - * @param repository - * the repository - * @param isTemp - * Whether this is a temporary pool used to resolve references - */ - protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository, boolean isTemp) - { - super(src); - this.repository = repository; - this.classLoader = new WeakReference(cl); - if (cl != null) { - classPath = new LoaderClassPath(cl); - this.insertClassPath(classPath); - } - childFirstLookup = true; - if (!isTemp && cl == null) - { - isBootstrapCl = true; - } - } - - /** - * Get the class loader - * - * @return the class loader - */ - public ClassLoader getClassLoader() { - ClassLoader cl = getClassLoader0(); - if (cl == null && !isBootstrapCl) - { - throw new IllegalStateException( - "ClassLoader has been garbage collected"); - } - return cl; - } - - protected ClassLoader getClassLoader0() { - return (ClassLoader)classLoader.get(); - } - - /** - * Close the class pool - */ - public void close() { - this.removeClassPath(classPath); - classPath.close(); - classes.clear(); - softcache.clear(); - } - - /** - * Flush a class - * - * @param classname - * the class to flush - */ - public synchronized void flushClass(String classname) { - classes.remove(classname); - softcache.remove(classname); - } - - /** - * Soften a class - * - * @param clazz - * the class - */ - public synchronized void soften(CtClass clazz) { - if (repository.isPrune()) - clazz.prune(); - classes.remove(clazz.getName()); - softcache.put(clazz.getName(), clazz); - } - - /** - * Whether the classloader is loader - * - * @return false always - */ - public boolean isUnloadedClassLoader() { - return false; - } - - /** - * Get the cached class - * - * @param classname - * the class name - * @return the class - */ - protected CtClass getCached(String classname) { - CtClass clazz = getCachedLocally(classname); - if (clazz == null) { - boolean isLocal = false; - - ClassLoader dcl = getClassLoader0(); - if (dcl != null) { - final int lastIndex = classname.lastIndexOf('$'); - String classResourceName = null; - if (lastIndex < 0) { - classResourceName = classname.replaceAll("[\\.]", "/") - + ".class"; - } - else { - classResourceName = classname.substring(0, lastIndex) - .replaceAll("[\\.]", "/") - + classname.substring(lastIndex) + ".class"; - } - - isLocal = dcl.getResource(classResourceName) != null; - } - - if (!isLocal) { - Map registeredCLs = repository.getRegisteredCLs(); - synchronized (registeredCLs) { - Iterator it = registeredCLs.values().iterator(); - while (it.hasNext()) { - ScopedClassPool pool = (ScopedClassPool)it.next(); - if (pool.isUnloadedClassLoader()) { - repository.unregisterClassLoader(pool - .getClassLoader()); - continue; - } - - clazz = pool.getCachedLocally(classname); - if (clazz != null) { - return clazz; - } - } - } - } - } - // *NOTE* NEED TO TEST WHEN SUPERCLASS IS IN ANOTHER UCL!!!!!! - return clazz; - } - - /** - * Cache a class - * - * @param classname - * the class name - * @param c - * the ctClass - * @param dynamic - * whether the class is dynamically generated - */ - protected void cacheCtClass(String classname, CtClass c, boolean dynamic) { - if (dynamic) { - super.cacheCtClass(classname, c, dynamic); - } - else { - if (repository.isPrune()) - c.prune(); - softcache.put(classname, c); - } - } - - /** - * Lock a class into the cache - * - * @param c - * the class - */ - public void lockInCache(CtClass c) { - super.cacheCtClass(c.getName(), c, false); - } - - /** - * Whether the class is cached in this pooled - * - * @param classname - * the class name - * @return the cached class - */ - protected CtClass getCachedLocally(String classname) { - CtClass cached = (CtClass)classes.get(classname); - if (cached != null) - return cached; - synchronized (softcache) { - return (CtClass)softcache.get(classname); - } - } - - /** - * Get any local copy of the class - * - * @param classname - * the class name - * @return the class - * @throws NotFoundException - * when the class is not found - */ - public synchronized CtClass getLocally(String classname) - throws NotFoundException { - softcache.remove(classname); - CtClass clazz = (CtClass)classes.get(classname); - if (clazz == null) { - clazz = createCtClass(classname, true); - if (clazz == null) - throw new NotFoundException(classname); - super.cacheCtClass(classname, clazz, false); - } - - return clazz; - } - - /** - * Convert a javassist class to a java class - * - * @param ct - * the javassist class - * @param loader - * the loader - * @throws CannotCompileException - * for any error - */ - public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) - throws CannotCompileException { - // We need to pass up the classloader stored in this pool, as the - // default implementation uses the Thread context cl. - // In the case of JSP's in Tomcat, - // org.apache.jasper.servlet.JasperLoader will be stored here, while - // it's parent - // org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader is used as the Thread - // context cl. The invocation class needs to - // be generated in the JasperLoader classloader since in the case of - // method invocations, the package name will be - // the same as for the class generated from the jsp, i.e. - // org.apache.jsp. For classes belonging to org.apache.jsp, - // JasperLoader does NOT delegate to its parent if it cannot find them. - lockInCache(ct); - return super.toClass(ct, getClassLoader0(), domain); - } -} diff --git a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java b/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java deleted file mode 100644 index 2dd05b4..0000000 --- a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.scopedpool; - -import com.wenshuo.agent.javassist.ClassPool; - -/** - * A factory interface. - * - * @author Kabir Khan - * @version $Revision: 1.4 $ - */ -public interface ScopedClassPoolFactory { - /** - * Makes an instance. - */ - ScopedClassPool create(ClassLoader cl, ClassPool src, - ScopedClassPoolRepository repository); - - /** - * Makes an instance. - */ - ScopedClassPool create(ClassPool src, - ScopedClassPoolRepository repository); -} diff --git a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java b/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java deleted file mode 100644 index 1386fff..0000000 --- a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.scopedpool; - -import com.wenshuo.agent.javassist.ClassPool; - -/** - * An implementation of factory. - * - * @author Kabir Khan - * @version $Revision: 1.5 $ - */ -public class ScopedClassPoolFactoryImpl implements ScopedClassPoolFactory { - /** - * Makes an instance. - */ - public ScopedClassPool create(ClassLoader cl, ClassPool src, - ScopedClassPoolRepository repository) { - return new ScopedClassPool(cl, src, repository, false); - } - - /** - * Makes an instance. - */ - public ScopedClassPool create(ClassPool src, - ScopedClassPoolRepository repository) { - return new ScopedClassPool(null, src, repository, true); - } -} diff --git a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java b/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java deleted file mode 100644 index 4965cff..0000000 --- a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.scopedpool; - -import java.util.Map; - -import com.wenshuo.agent.javassist.ClassPool; - -/** - * An interface to ScopedClassPoolRepositoryImpl. - * - * @author Kabir Khan - * @version $Revision: 1.4 $ - */ -public interface ScopedClassPoolRepository { - /** - * Records a factory. - */ - void setClassPoolFactory(ScopedClassPoolFactory factory); - - /** - * Obtains the recorded factory. - */ - ScopedClassPoolFactory getClassPoolFactory(); - - /** - * Returns whether or not the class pool is pruned. - * - * @return the prune. - */ - boolean isPrune(); - - /** - * Sets the prune flag. - * - * @param prune a new value. - */ - void setPrune(boolean prune); - - /** - * Create a scoped classpool. - * - * @param cl the classloader. - * @param src the original classpool. - * @return the classpool. - */ - ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src); - - /** - * Finds a scoped classpool registered under the passed in classloader. - * - * @param cl the classloader. - * @return the classpool. - */ - ClassPool findClassPool(ClassLoader cl); - - /** - * Register a classloader. - * - * @param ucl the classloader. - * @return the classpool. - */ - ClassPool registerClassLoader(ClassLoader ucl); - - /** - * Get the registered classloaders. - * - * @return the registered classloaders. - */ - Map getRegisteredCLs(); - - /** - * This method will check to see if a register classloader has been - * undeployed (as in JBoss). - */ - void clearUnregisteredClassLoaders(); - - /** - * Unregisters a classpool and unregisters its classloader. - * - * @param cl the classloader the pool is stored under. - */ - void unregisterClassLoader(ClassLoader cl); -} diff --git a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java b/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java deleted file mode 100644 index 3f0e5ae..0000000 --- a/src/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.scopedpool; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; -import java.util.WeakHashMap; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.LoaderClassPath; - -/** - * An implementation of ScopedClassPoolRepository. - * It is an singleton. - * - * @author Kabir Khan - * @version $Revision: 1.4 $ - */ -public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository { - /** The instance */ - private static final ScopedClassPoolRepositoryImpl instance = new ScopedClassPoolRepositoryImpl(); - - /** Whether to prune */ - private boolean prune = true; - - /** Whether to prune when added to the classpool's cache */ - boolean pruneWhenCached; - - /** The registered classloaders */ - protected Map registeredCLs = Collections - .synchronizedMap(new WeakHashMap()); - - /** The default class pool */ - protected ClassPool classpool; - - /** The factory for creating class pools */ - protected ScopedClassPoolFactory factory = new ScopedClassPoolFactoryImpl(); - - /** - * Get the instance. - * - * @return the instance. - */ - public static ScopedClassPoolRepository getInstance() { - return instance; - } - - /** - * Singleton. - */ - private ScopedClassPoolRepositoryImpl() { - classpool = ClassPool.getDefault(); - // FIXME This doesn't look correct - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - classpool.insertClassPath(new LoaderClassPath(cl)); - } - - /** - * Returns the value of the prune attribute. - * - * @return the prune. - */ - public boolean isPrune() { - return prune; - } - - /** - * Set the prune attribute. - * - * @param prune a new value. - */ - public void setPrune(boolean prune) { - this.prune = prune; - } - - /** - * Create a scoped classpool. - * - * @param cl the classloader. - * @param src the original classpool. - * @return the classpool - */ - public ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src) { - return factory.create(cl, src, this); - } - - public ClassPool findClassPool(ClassLoader cl) { - if (cl == null) - return registerClassLoader(ClassLoader.getSystemClassLoader()); - - return registerClassLoader(cl); - } - - /** - * Register a classloader. - * - * @param ucl the classloader. - * @return the classpool - */ - public ClassPool registerClassLoader(ClassLoader ucl) { - synchronized (registeredCLs) { - // FIXME: Probably want to take this method out later - // so that AOP framework can be independent of JMX - // This is in here so that we can remove a UCL from the ClassPool as - // a - // ClassPool.classpath - if (registeredCLs.containsKey(ucl)) { - return (ClassPool)registeredCLs.get(ucl); - } - ScopedClassPool pool = createScopedClassPool(ucl, classpool); - registeredCLs.put(ucl, pool); - return pool; - } - } - - /** - * Get the registered classloaders. - */ - public Map getRegisteredCLs() { - clearUnregisteredClassLoaders(); - return registeredCLs; - } - - /** - * This method will check to see if a register classloader has been - * undeployed (as in JBoss) - */ - public void clearUnregisteredClassLoaders() { - ArrayList toUnregister = null; - synchronized (registeredCLs) { - Iterator it = registeredCLs.values().iterator(); - while (it.hasNext()) { - ScopedClassPool pool = (ScopedClassPool)it.next(); - if (pool.isUnloadedClassLoader()) { - it.remove(); - ClassLoader cl = pool.getClassLoader(); - if (cl != null) { - if (toUnregister == null) { - toUnregister = new ArrayList(); - } - toUnregister.add(cl); - } - } - } - if (toUnregister != null) { - for (int i = 0; i < toUnregister.size(); i++) { - unregisterClassLoader((ClassLoader)toUnregister.get(i)); - } - } - } - } - - public void unregisterClassLoader(ClassLoader cl) { - synchronized (registeredCLs) { - ScopedClassPool pool = (ScopedClassPool)registeredCLs.remove(cl); - if (pool != null) - pool.close(); - } - } - - public void insertDelegate(ScopedClassPoolRepository delegate) { - // Noop - this is the end - } - - public void setClassPoolFactory(ScopedClassPoolFactory factory) { - this.factory = factory; - } - - public ScopedClassPoolFactory getClassPoolFactory() { - return factory; - } -} diff --git a/src/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java b/src/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java deleted file mode 100644 index 3743ca6..0000000 --- a/src/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.scopedpool; - -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * This Map will remove entries when the value in the map has been cleaned from - * garbage collection - * - * @version $Revision: 1.4 $ - * @author Bill Burke - */ -public class SoftValueHashMap extends AbstractMap implements Map { - private static class SoftValueRef extends SoftReference { - public Object key; - - private SoftValueRef(Object key, Object val, ReferenceQueue q) { - super(val, q); - this.key = key; - } - - private static SoftValueRef create(Object key, Object val, - ReferenceQueue q) { - if (val == null) - return null; - else - return new SoftValueRef(key, val, q); - } - - } - - /** - * Returns a set of the mappings contained in this hash table. - */ - public Set entrySet() { - processQueue(); - return hash.entrySet(); - } - - /* Hash table mapping WeakKeys to values */ - private Map hash; - - /* Reference queue for cleared WeakKeys */ - private ReferenceQueue queue = new ReferenceQueue(); - - /* - * Remove all invalidated entries from the map, that is, remove all entries - * whose values have been discarded. - */ - private void processQueue() { - SoftValueRef ref; - while ((ref = (SoftValueRef)queue.poll()) != null) { - if (ref == (SoftValueRef)hash.get(ref.key)) { - // only remove if it is the *exact* same WeakValueRef - // - hash.remove(ref.key); - } - } - } - - /* -- Constructors -- */ - - /** - * Constructs a new, empty WeakHashMap with the given initial - * capacity and the given load factor. - * - * @param initialCapacity - * The initial capacity of the WeakHashMap - * - * @param loadFactor - * The load factor of the WeakHashMap - * - * @throws IllegalArgumentException - * If the initial capacity is less than zero, or if the load - * factor is nonpositive - */ - public SoftValueHashMap(int initialCapacity, float loadFactor) { - hash = new HashMap(initialCapacity, loadFactor); - } - - /** - * Constructs a new, empty WeakHashMap with the given initial - * capacity and the default load factor, which is 0.75. - * - * @param initialCapacity - * The initial capacity of the WeakHashMap - * - * @throws IllegalArgumentException - * If the initial capacity is less than zero - */ - public SoftValueHashMap(int initialCapacity) { - hash = new HashMap(initialCapacity); - } - - /** - * Constructs a new, empty WeakHashMap with the default - * initial capacity and the default load factor, which is 0.75. - */ - public SoftValueHashMap() { - hash = new HashMap(); - } - - /** - * Constructs a new WeakHashMap with the same mappings as the - * specified Map. The WeakHashMap is created with - * an initial capacity of twice the number of mappings in the specified map - * or 11 (whichever is greater), and a default load factor, which is - * 0.75. - * - * @param t the map whose mappings are to be placed in this map. - */ - public SoftValueHashMap(Map t) { - this(Math.max(2 * t.size(), 11), 0.75f); - putAll(t); - } - - /* -- Simple queries -- */ - - /** - * Returns the number of key-value mappings in this map. Note: - * In contrast with most implementations of the - * Map interface, the time required by this operation is - * linear in the size of the map. - */ - public int size() { - processQueue(); - return hash.size(); - } - - /** - * Returns true if this map contains no key-value mappings. - */ - public boolean isEmpty() { - processQueue(); - return hash.isEmpty(); - } - - /** - * Returns true if this map contains a mapping for the - * specified key. - * - * @param key - * The key whose presence in this map is to be tested. - */ - public boolean containsKey(Object key) { - processQueue(); - return hash.containsKey(key); - } - - /* -- Lookup and modification operations -- */ - - /** - * Returns the value to which this map maps the specified key. - * If this map does not contain a value for this key, then return - * null. - * - * @param key - * The key whose associated value, if any, is to be returned. - */ - public Object get(Object key) { - processQueue(); - SoftReference ref = (SoftReference)hash.get(key); - if (ref != null) - return ref.get(); - return null; - } - - /** - * Updates this map so that the given key maps to the given - * value. If the map previously contained a mapping for - * key then that mapping is replaced and the previous value - * is returned. - * - * @param key - * The key that is to be mapped to the given value - * @param value - * The value to which the given key is to be - * mapped - * - * @return The previous value to which this key was mapped, or - * null if if there was no mapping for the key - */ - public Object put(Object key, Object value) { - processQueue(); - Object rtn = hash.put(key, SoftValueRef.create(key, value, queue)); - if (rtn != null) - rtn = ((SoftReference)rtn).get(); - return rtn; - } - - /** - * Removes the mapping for the given key from this map, if - * present. - * - * @param key - * The key whose mapping is to be removed. - * - * @return The value to which this key was mapped, or null if - * there was no mapping for the key. - */ - public Object remove(Object key) { - processQueue(); - return hash.remove(key); - } - - /** - * Removes all mappings from this map. - */ - public void clear() { - processQueue(); - hash.clear(); - } -} diff --git a/src/com/wenshuo/agent/javassist/scopedpool/package.html b/src/com/wenshuo/agent/javassist/scopedpool/package.html deleted file mode 100644 index 946e5e1..0000000 --- a/src/com/wenshuo/agent/javassist/scopedpool/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -

A custom class pool for several JBoss products. -It is not part of Javassist. -

- - diff --git a/src/com/wenshuo/agent/javassist/tools/Callback.java b/src/com/wenshuo/agent/javassist/tools/Callback.java deleted file mode 100644 index 56ba4fb..0000000 --- a/src/com/wenshuo/agent/javassist/tools/Callback.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools; - -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.CtBehavior; - -import java.util.HashMap; -import java.util.UUID; - -/** - * Creates bytecode that when executed calls back to the instance's result method. - * - *

Example of how to create and insert a callback:

- *
- * ctMethod.insertAfter(new Callback("Thread.currentThread()") {
- *     public void result(Object... objects) {
- *         Thread thread = (Thread) objects[0];
- *         // do something with thread...
- *     }
- * }.sourceCode());
- * 
- *

Contains utility methods for inserts callbacks in CtBehaviour, example:

- *
- * insertAfter(ctBehaviour, new Callback("Thread.currentThread(), dummyString") {
- *     public void result(Object... objects) {
- *         Thread thread = (Thread) objects[0];
- *         // do something with thread...
- *     }
- * });
- * 
- * - * @author Marten Hedborg - */ -public abstract class Callback { - - public static HashMap callbacks = new HashMap(); - - private final String sourceCode; - - /** - * Constructs a new Callback object. - * - * @param src The source code representing the inserted callback bytecode. - * Can be one or many single statements each returning one object. - * If many single statements are used they must be comma separated. - */ - public Callback(String src){ - String uuid = UUID.randomUUID().toString(); - callbacks.put(uuid, this); - sourceCode = "((javassist.tools.Callback) javassist.tools.Callback.callbacks.get(\""+uuid+"\")).result(new Object[]{"+src+"});"; - } - - /** - * Gets called when bytecode is executed - * - * @param objects Objects that the bytecode in callback returns - */ - public abstract void result(Object... objects); - - @Override - public String toString(){ - return sourceCode(); - } - - public String sourceCode(){ - return sourceCode; - } - - /** - * Utility method to insert callback at the beginning of the body. - * - * @param callback The callback - * - * @see CtBehavior#insertBefore(String) - */ - public static void insertBefore(CtBehavior behavior, Callback callback) - throws CannotCompileException - { - behavior.insertBefore(callback.toString()); - } - - /** - * Utility method to inserts callback at the end of the body. - * The callback is inserted just before every return instruction. - * It is not executed when an exception is thrown. - * - * @param behavior The behaviour to insert callback in - * @param callback The callback - * - * @see CtBehavior#insertAfter(String, boolean) - */ - public static void insertAfter(CtBehavior behavior,Callback callback) - throws CannotCompileException - { - behavior.insertAfter(callback.toString(), false); - } - - /** - * Utility method to inserts callback at the end of the body. - * The callback is inserted just before every return instruction. - * It is not executed when an exception is thrown. - * - * @param behavior The behaviour to insert callback in - * @param callback The callback representing the inserted. - * @param asFinally True if the inserted is executed - * Not only when the control normally returns - * but also when an exception is thrown. - * If this parameter is true, the inserted code cannot - * access local variables. - * - * @see CtBehavior#insertAfter(String, boolean) - */ - public static void insertAfter(CtBehavior behavior, Callback callback, boolean asFinally) - throws CannotCompileException - { - behavior.insertAfter(callback.toString(), asFinally); - } - - /** - * Utility method to inserts callback at the specified line in the body. - * - * @param behavior The behaviour to insert callback in - * @param callback The callback representing. - * @param lineNum The line number. The callback is inserted at the - * beginning of the code at the line specified by this - * line number. - * - * @return The line number at which the callback has been inserted. - * - * @see CtBehavior#insertAt(int, String) - */ - public static int insertAt(CtBehavior behavior, Callback callback, int lineNum) - throws CannotCompileException - { - return behavior.insertAt(lineNum, callback.toString()); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/Dump.java b/src/com/wenshuo/agent/javassist/tools/Dump.java deleted file mode 100644 index 8a504ee..0000000 --- a/src/com/wenshuo/agent/javassist/tools/Dump.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools; - -import java.io.*; -import com.wenshuo.agent.javassist.bytecode.ClassFile; -import com.wenshuo.agent.javassist.bytecode.ClassFilePrinter; - -/** - * Dump is a tool for viewing the class definition in the given - * class file. Unlike the JDK javap tool, Dump works even if - * the class file is broken. - * - *

For example, - *

% java javassist.tools.Dump foo.class
- * - *

prints the contents of the constant pool and the list of methods - * and fields. - */ -public class Dump { - private Dump() {} - - /** - * Main method. - * - * @param args args[0] is the class file name. - */ - public static void main(String[] args) throws Exception { - if (args.length != 1) { - System.err.println("Usage: java Dump "); - return; - } - - DataInputStream in = new DataInputStream( - new FileInputStream(args[0])); - ClassFile w = new ClassFile(in); - PrintWriter out = new PrintWriter(System.out, true); - out.println("*** constant pool ***"); - w.getConstPool().print(out); - out.println(); - out.println("*** members ***"); - ClassFilePrinter.print(w, out); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/framedump.java b/src/com/wenshuo/agent/javassist/tools/framedump.java deleted file mode 100644 index 623ffc3..0000000 --- a/src/com/wenshuo/agent/javassist/tools/framedump.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.tools; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.bytecode.analysis.FramePrinter; - -/** - * framedump is a tool for viewing a merged combination of the instructions and frame state - * of all methods in a class. - * - *

For example, - *

% java javassist.tools.framedump foo.class
- */ -public class framedump { - private framedump() {} - - /** - * Main method. - * - * @param args args[0] is the class file name. - */ - public static void main(String[] args) throws Exception { - if (args.length != 1) { - System.err.println("Usage: java javassist.tools.framedump "); - return; - } - - ClassPool pool = ClassPool.getDefault(); - CtClass clazz = pool.get(args[0]); - System.out.println("Frame Dump of " + clazz.getName() + ":"); - FramePrinter.print(clazz, System.out); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/package.html b/src/com/wenshuo/agent/javassist/tools/package.html deleted file mode 100644 index bee6208..0000000 --- a/src/com/wenshuo/agent/javassist/tools/package.html +++ /dev/null @@ -1,6 +0,0 @@ - - -Covenient tools. - - - diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java b/src/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java deleted file mode 100644 index de3c782..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -/** - * Signals that ClassMetaobject.newInstance() fails. - */ -public class CannotCreateException extends Exception { - public CannotCreateException(String s) { - super(s); - } - - public CannotCreateException(Exception e) { - super("by " + e.toString()); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java b/src/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java deleted file mode 100644 index 7907022..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -import java.lang.reflect.InvocationTargetException; -import java.lang.IllegalAccessException; - -/** - * Thrown when method invocation using the reflection API has thrown - * an exception. - * - * @see javassist.tools.reflect.Metaobject#trapMethodcall(int, Object[]) - * @see javassist.tools.reflect.ClassMetaobject#trapMethodcall(int, Object[]) - * @see javassist.tools.reflect.ClassMetaobject#invoke(Object, int, Object[]) - */ -public class CannotInvokeException extends RuntimeException { - - private Throwable err = null; - - /** - * Returns the cause of this exception. It may return null. - */ - public Throwable getReason() { return err; } - - /** - * Constructs a CannotInvokeException with an error message. - */ - public CannotInvokeException(String reason) { - super(reason); - } - - /** - * Constructs a CannotInvokeException with an InvocationTargetException. - */ - public CannotInvokeException(InvocationTargetException e) { - super("by " + e.getTargetException().toString()); - err = e.getTargetException(); - } - - /** - * Constructs a CannotInvokeException with an IllegalAccessException. - */ - public CannotInvokeException(IllegalAccessException e) { - super("by " + e.toString()); - err = e; - } - - /** - * Constructs a CannotInvokeException with an ClassNotFoundException. - */ - public CannotInvokeException(ClassNotFoundException e) { - super("by " + e.toString()); - err = e; - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java b/src/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java deleted file mode 100644 index f2673c9..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -import com.wenshuo.agent.javassist.CannotCompileException; - -/** - * Thrown by makeReflective() in Reflection - * when there is an attempt to reflect - * a class that is either an interface or a subclass of - * either ClassMetaobject or Metaobject. - * - * @author Brett Randall - * @see javassist.tools.reflect.Reflection#makeReflective(CtClass,CtClass,CtClass) - * @see javassist.CannotCompileException - */ -public class CannotReflectException extends CannotCompileException { - public CannotReflectException(String msg) { - super(msg); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java b/src/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java deleted file mode 100644 index 78fb42f..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -import java.lang.reflect.*; -import java.util.Arrays; -import java.io.Serializable; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -/** - * A runtime class metaobject. - * - *

A ClassMetaobject is created for every - * class of reflective objects. It can be used to hold values - * shared among the reflective objects of the same class. - * - *

To obtain a class metaobject, calls _getClass() - * on a reflective object. For example, - * - *

ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass();
- * 
- * - * @see javassist.tools.reflect.Metaobject - * @see javassist.tools.reflect.Metalevel - */ -public class ClassMetaobject implements Serializable { - /** - * The base-level methods controlled by a metaobject - * are renamed so that they begin with - * methodPrefix "_m_". - */ - static final String methodPrefix = "_m_"; - static final int methodPrefixLen = 3; - - private Class javaClass; - private Constructor[] constructors; - private Method[] methods; - - /** - * Specifies how a java.lang.Class object is loaded. - * - *

If true, it is loaded by: - *

Thread.currentThread().getContextClassLoader().loadClass()
- *

If false, it is loaded by Class.forName(). - * The default value is false. - */ - public static boolean useContextClassLoader = false; - - /** - * Constructs a ClassMetaobject. - * - * @param params params[0] is the name of the class - * of the reflective objects. - */ - public ClassMetaobject(String[] params) - { - try { - javaClass = getClassObject(params[0]); - } - catch (ClassNotFoundException e) { - throw new RuntimeException("not found: " + params[0] - + ", useContextClassLoader: " - + Boolean.toString(useContextClassLoader), e); - } - - constructors = javaClass.getConstructors(); - methods = null; - } - - private void writeObject(ObjectOutputStream out) throws IOException { - out.writeUTF(javaClass.getName()); - } - - private void readObject(ObjectInputStream in) - throws IOException, ClassNotFoundException - { - javaClass = getClassObject(in.readUTF()); - constructors = javaClass.getConstructors(); - methods = null; - } - - private Class getClassObject(String name) throws ClassNotFoundException { - if (useContextClassLoader) - return Thread.currentThread().getContextClassLoader() - .loadClass(name); - else - return Class.forName(name); - } - - /** - * Obtains the java.lang.Class representing this class. - */ - public final Class getJavaClass() { - return javaClass; - } - - /** - * Obtains the name of this class. - */ - public final String getName() { - return javaClass.getName(); - } - - /** - * Returns true if obj is an instance of this class. - */ - public final boolean isInstance(Object obj) { - return javaClass.isInstance(obj); - } - - /** - * Creates a new instance of the class. - * - * @param args the arguments passed to the constructor. - */ - public final Object newInstance(Object[] args) - throws CannotCreateException - { - int n = constructors.length; - for (int i = 0; i < n; ++i) { - try { - return constructors[i].newInstance(args); - } - catch (IllegalArgumentException e) { - // try again - } - catch (InstantiationException e) { - throw new CannotCreateException(e); - } - catch (IllegalAccessException e) { - throw new CannotCreateException(e); - } - catch (InvocationTargetException e) { - throw new CannotCreateException(e); - } - } - - throw new CannotCreateException("no constructor matches"); - } - - /** - * Is invoked when static fields of the base-level - * class are read and the runtime system intercepts it. - * This method simply returns the value of the field. - * - *

Every subclass of this class should redefine this method. - */ - public Object trapFieldRead(String name) { - Class jc = getJavaClass(); - try { - return jc.getField(name).get(null); - } - catch (NoSuchFieldException e) { - throw new RuntimeException(e.toString()); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Is invoked when static fields of the base-level - * class are modified and the runtime system intercepts it. - * This method simply sets the field to the given value. - * - *

Every subclass of this class should redefine this method. - */ - public void trapFieldWrite(String name, Object value) { - Class jc = getJavaClass(); - try { - jc.getField(name).set(null, value); - } - catch (NoSuchFieldException e) { - throw new RuntimeException(e.toString()); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Invokes a method whose name begins with - * methodPrefix "_m_" and the identifier. - * - * @exception CannotInvokeException if the invocation fails. - */ - static public Object invoke(Object target, int identifier, Object[] args) - throws Throwable - { - Method[] allmethods = target.getClass().getMethods(); - int n = allmethods.length; - String head = methodPrefix + identifier; - for (int i = 0; i < n; ++i) - if (allmethods[i].getName().startsWith(head)) { - try { - return allmethods[i].invoke(target, args); - } catch (java.lang.reflect.InvocationTargetException e) { - throw e.getTargetException(); - } catch (java.lang.IllegalAccessException e) { - throw new CannotInvokeException(e); - } - } - - throw new CannotInvokeException("cannot find a method"); - } - - /** - * Is invoked when static methods of the base-level - * class are called and the runtime system intercepts it. - * This method simply executes the intercepted method invocation - * with the original parameters and returns the resulting value. - * - *

Every subclass of this class should redefine this method. - */ - public Object trapMethodcall(int identifier, Object[] args) - throws Throwable - { - try { - Method[] m = getReflectiveMethods(); - return m[identifier].invoke(null, args); - } - catch (java.lang.reflect.InvocationTargetException e) { - throw e.getTargetException(); - } - catch (java.lang.IllegalAccessException e) { - throw new CannotInvokeException(e); - } - } - - /** - * Returns an array of the methods defined on the given reflective - * object. This method is for the internal use only. - */ - public final Method[] getReflectiveMethods() { - if (methods != null) - return methods; - - Class baseclass = getJavaClass(); - Method[] allmethods = baseclass.getDeclaredMethods(); - int n = allmethods.length; - int[] index = new int[n]; - int max = 0; - for (int i = 0; i < n; ++i) { - Method m = allmethods[i]; - String mname = m.getName(); - if (mname.startsWith(methodPrefix)) { - int k = 0; - for (int j = methodPrefixLen;; ++j) { - char c = mname.charAt(j); - if ('0' <= c && c <= '9') - k = k * 10 + c - '0'; - else - break; - } - - index[i] = ++k; - if (k > max) - max = k; - } - } - - methods = new Method[max]; - for (int i = 0; i < n; ++i) - if (index[i] > 0) - methods[index[i] - 1] = allmethods[i]; - - return methods; - } - - /** - * Returns the java.lang.reflect.Method object representing - * the method specified by identifier. - * - *

Note that the actual method returned will be have an altered, - * reflective name i.e. _m_2_... - * - * @param identifier the identifier index - * given to trapMethodcall() etc. - * @see Metaobject#trapMethodcall(int,Object[]) - * @see #trapMethodcall(int,Object[]) - */ - public final Method getMethod(int identifier) { - return getReflectiveMethods()[identifier]; - } - - /** - * Returns the name of the method specified - * by identifier. - */ - public final String getMethodName(int identifier) { - String mname = getReflectiveMethods()[identifier].getName(); - int j = ClassMetaobject.methodPrefixLen; - for (;;) { - char c = mname.charAt(j++); - if (c < '0' || '9' < c) - break; - } - - return mname.substring(j); - } - - /** - * Returns an array of Class objects representing the - * formal parameter types of the method specified - * by identifier. - */ - public final Class[] getParameterTypes(int identifier) { - return getReflectiveMethods()[identifier].getParameterTypes(); - } - - /** - * Returns a Class objects representing the - * return type of the method specified by identifier. - */ - public final Class getReturnType(int identifier) { - return getReflectiveMethods()[identifier].getReturnType(); - } - - /** - * Returns the identifier index of the method, as identified by its - * original name. - * - *

This method is useful, in conjuction with - * {@link ClassMetaobject#getMethod(int)}, to obtain a quick reference - * to the original method in the reflected class (i.e. not the proxy - * method), using the original name of the method. - * - *

Written by Brett Randall and Shigeru Chiba. - * - * @param originalName The original name of the reflected method - * @param argTypes array of Class specifying the method signature - * @return the identifier index of the original method - * @throws NoSuchMethodException if the method does not exist - * - * @see ClassMetaobject#getMethod(int) - */ - public final int getMethodIndex(String originalName, Class[] argTypes) - throws NoSuchMethodException - { - Method[] mthds = getReflectiveMethods(); - for (int i = 0; i < mthds.length; i++) { - if (mthds[i] == null) - continue; - - // check name and parameter types match - if (getMethodName(i).equals(originalName) - && Arrays.equals(argTypes, mthds[i].getParameterTypes())) - return i; - } - - throw new NoSuchMethodException("Method " + originalName - + " not found"); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/Compiler.java b/src/com/wenshuo/agent/javassist/tools/reflect/Compiler.java deleted file mode 100644 index 0953163..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/Compiler.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.ClassPool; -import java.io.PrintStream; - -class CompiledClass { - public String classname; - public String metaobject; - public String classobject; -} - -/** - * A bytecode translator for reflection. - * - *

This translator directly modifies class files on a local disk so that - * the classes represented by those class files are reflective. - * After the modification, the class files can be run with the standard JVM - * without javassist.tools.reflect.Loader - * or any other user-defined class loader. - * - *

The modified class files are given as the command-line parameters, - * which are a sequence of fully-qualified class names followed by options: - * - *

-m classname : specifies the class of the - * metaobjects associated with instances of the class followed by - * this option. The default is javassit.reflect.Metaobject. - * - *

-c classname : specifies the class of the - * class metaobjects associated with instances of the class followed by - * this option. The default is javassit.reflect.ClassMetaobject. - * - *

If a class name is not followed by any options, the class indicated - * by that class name is not reflective. - * - *

For example, - *

% java Compiler Dog -m MetaDog -c CMetaDog Cat -m MetaCat Cow
- * 
- * - *

modifies class files Dog.class, Cat.class, - * and Cow.class. - * The metaobject of a Dog object is a MetaDog object and the class - * metaobject is a CMetaDog object. - * The metaobject of a Cat object is a MetaCat object but - * the class metaobject is a default one. - * Cow objects are not reflective. - * - *

Note that if the super class is also made reflective, it must be done - * before the sub class. - * - * @see javassist.tools.reflect.Metaobject - * @see javassist.tools.reflect.ClassMetaobject - * @see javassist.tools.reflect.Reflection - */ -public class Compiler { - - public static void main(String[] args) throws Exception { - if (args.length == 0) { - help(System.err); - return; - } - - CompiledClass[] entries = new CompiledClass[args.length]; - int n = parse(args, entries); - - if (n < 1) { - System.err.println("bad parameter."); - return; - } - - processClasses(entries, n); - } - - private static void processClasses(CompiledClass[] entries, int n) - throws Exception - { - Reflection implementor = new Reflection(); - ClassPool pool = ClassPool.getDefault(); - implementor.start(pool); - - for (int i = 0; i < n; ++i) { - CtClass c = pool.get(entries[i].classname); - if (entries[i].metaobject != null - || entries[i].classobject != null) { - String metaobj, classobj; - - if (entries[i].metaobject == null) - metaobj = "javassist.tools.reflect.Metaobject"; - else - metaobj = entries[i].metaobject; - - if (entries[i].classobject == null) - classobj = "javassist.tools.reflect.ClassMetaobject"; - else - classobj = entries[i].classobject; - - if (!implementor.makeReflective(c, pool.get(metaobj), - pool.get(classobj))) - System.err.println("Warning: " + c.getName() - + " is reflective. It was not changed."); - - System.err.println(c.getName() + ": " + metaobj + ", " - + classobj); - } - else - System.err.println(c.getName() + ": not reflective"); - } - - for (int i = 0; i < n; ++i) { - implementor.onLoad(pool, entries[i].classname); - pool.get(entries[i].classname).writeFile(); - } - } - - private static int parse(String[] args, CompiledClass[] result) { - int n = -1; - for (int i = 0; i < args.length; ++i) { - String a = args[i]; - if (a.equals("-m")) - if (n < 0 || i + 1 > args.length) - return -1; - else - result[n].metaobject = args[++i]; - else if (a.equals("-c")) - if (n < 0 || i + 1 > args.length) - return -1; - else - result[n].classobject = args[++i]; - else if (a.charAt(0) == '-') - return -1; - else { - CompiledClass cc = new CompiledClass(); - cc.classname = a; - cc.metaobject = null; - cc.classobject = null; - result[++n] = cc; - } - } - - return n + 1; - } - - private static void help(PrintStream out) { - out.println("Usage: java javassist.tools.reflect.Compiler"); - out.println(" ( [-m ] [-c ])+"); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/Loader.java b/src/com/wenshuo/agent/javassist/tools/reflect/Loader.java deleted file mode 100644 index 4b9d1e3..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/Loader.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.ClassPool; - -/** - * A class loader for reflection. - * - *

To run a program, say MyApp, - * including a reflective class, - * you must write a start-up program as follows: - * - *

- * public class Main {
- *   public static void main(String[] args) throws Throwable {
- *     javassist.tools.reflect.Loader cl
- *         = (javassist.tools.reflect.Loader)Main.class.getClassLoader();
- *     cl.makeReflective("Person", "MyMetaobject",
- *                       "javassist.tools.reflect.ClassMetaobject");
- *     cl.run("MyApp", args);
- *   }
- * }
- * 
- * - *

Then run this program as follows: - * - *

% java javassist.tools.reflect.Loader Main arg1, ...
- * - *

This command runs Main.main() with arg1, ... - * and Main.main() runs MyApp.main() with - * arg1, ... - * The Person class is modified - * to be a reflective class. Method calls on a Person - * object are intercepted by an instance of MyMetaobject. - * - *

Also, you can run MyApp in a slightly different way: - * - *

- * public class Main2 {
- *   public static void main(String[] args) throws Throwable {
- *     javassist.tools.reflect.Loader cl = new javassist.tools.reflect.Loader();
- *     cl.makeReflective("Person", "MyMetaobject",
- *                       "javassist.tools.reflect.ClassMetaobject");
- *     cl.run("MyApp", args);
- *   }
- * }
- * 
- * - *

This program is run as follows: - * - *

% java Main2 arg1, ...
- * - *

The difference from the former one is that the class Main - * is loaded by javassist.tools.reflect.Loader whereas the class - * Main2 is not. Thus, Main belongs - * to the same name space (security domain) as MyApp - * whereas Main2 does not; Main2 belongs - * to the same name space as javassist.tools.reflect.Loader. - * For more details, - * see the notes in the manual page of javassist.Loader. - * - *

The class Main2 is equivalent to this class: - * - *

- * public class Main3 {
- *   public static void main(String[] args) throws Throwable {
- *     Reflection reflection = new Reflection();
- *     javassist.Loader cl
- *         = new javassist.Loader(ClassPool.getDefault(reflection));
- *     reflection.makeReflective("Person", "MyMetaobject",
- *                               "javassist.tools.reflect.ClassMetaobject");
- *     cl.run("MyApp", args);
- *   }
- * }
- * 
- * - *

Note: - * - *

javassist.tools.reflect.Loader does not make a class reflective - * if that class is in a java.* or - * javax.* pacakge because of the specifications - * on the class loading algorithm of Java. The JVM does not allow to - * load such a system class with a user class loader. - * - *

To avoid this limitation, those classes should be statically - * modified with javassist.tools.reflect.Compiler and the original - * class files should be replaced. - * - * @see javassist.tools.reflect.Reflection - * @see javassist.tools.reflect.Compiler - * @see javassist.Loader - */ -public class Loader extends com.wenshuo.agent.javassist.Loader { - protected Reflection reflection; - - /** - * Loads a class with an instance of Loader - * and calls main() in that class. - * - * @param args command line parameters. - *
  args[0] is the class name to be loaded. - *
  args[1..n] are parameters passed - * to the target main(). - */ - public static void main(String[] args) throws Throwable { - Loader cl = new Loader(); - cl.run(args); - } - - /** - * Constructs a new class loader. - */ - public Loader() throws CannotCompileException, NotFoundException { - super(); - delegateLoadingOf("javassist.tools.reflect.Loader"); - - reflection = new Reflection(); - ClassPool pool = ClassPool.getDefault(); - addTranslator(pool, reflection); - } - - /** - * Produces a reflective class. - * If the super class is also made reflective, it must be done - * before the sub class. - * - * @param clazz the reflective class. - * @param metaobject the class of metaobjects. - * It must be a subclass of - * Metaobject. - * @param metaclass the class of the class metaobject. - * It must be a subclass of - * ClassMetaobject. - * @return false if the class is already reflective. - * - * @see javassist.tools.reflect.Metaobject - * @see javassist.tools.reflect.ClassMetaobject - */ - public boolean makeReflective(String clazz, - String metaobject, String metaclass) - throws CannotCompileException, NotFoundException - { - return reflection.makeReflective(clazz, metaobject, metaclass); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java b/src/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java deleted file mode 100644 index d8c1f4a..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -/** - * An interface to access a metaobject and a class metaobject. - * This interface is implicitly implemented by the reflective - * class. - */ -public interface Metalevel { - /** - * Obtains the class metaobject associated with this object. - */ - ClassMetaobject _getClass(); - - /** - * Obtains the metaobject associated with this object. - */ - Metaobject _getMetaobject(); - - /** - * Changes the metaobject associated with this object. - */ - void _setMetaobject(Metaobject m); -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java b/src/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java deleted file mode 100644 index f96444e..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -import java.lang.reflect.Method; -import java.io.Serializable; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -/** - * A runtime metaobject. - * - *

A Metaobject is created for - * every object at the base level. A different reflective object is - * associated with a different metaobject. - * - *

The metaobject intercepts method calls - * on the reflective object at the base-level. To change the behavior - * of the method calls, a subclass of Metaobject - * should be defined. - * - *

To obtain a metaobject, calls _getMetaobject() - * on a reflective object. For example, - * - *

- * Metaobject m = ((Metalevel)reflectiveObject)._getMetaobject();
- * 
- * - * @see javassist.tools.reflect.ClassMetaobject - * @see javassist.tools.reflect.Metalevel - */ -public class Metaobject implements Serializable { - protected ClassMetaobject classmetaobject; - protected Metalevel baseobject; - protected Method[] methods; - - /** - * Constructs a Metaobject. The metaobject is - * constructed before the constructor is called on the base-level - * object. - * - * @param self the object that this metaobject is associated with. - * @param args the parameters passed to the constructor of - * self. - */ - public Metaobject(Object self, Object[] args) { - baseobject = (Metalevel)self; - classmetaobject = baseobject._getClass(); - methods = classmetaobject.getReflectiveMethods(); - } - - /** - * Constructs a Metaobject without initialization. - * If calling this constructor, a subclass should be responsible - * for initialization. - */ - protected Metaobject() { - baseobject = null; - classmetaobject = null; - methods = null; - } - - private void writeObject(ObjectOutputStream out) throws IOException { - out.writeObject(baseobject); - } - - private void readObject(ObjectInputStream in) - throws IOException, ClassNotFoundException - { - baseobject = (Metalevel)in.readObject(); - classmetaobject = baseobject._getClass(); - methods = classmetaobject.getReflectiveMethods(); - } - - /** - * Obtains the class metaobject associated with this metaobject. - * - * @see javassist.tools.reflect.ClassMetaobject - */ - public final ClassMetaobject getClassMetaobject() { - return classmetaobject; - } - - /** - * Obtains the object controlled by this metaobject. - */ - public final Object getObject() { - return baseobject; - } - - /** - * Changes the object controlled by this metaobject. - * - * @param self the object - */ - public final void setObject(Object self) { - baseobject = (Metalevel)self; - classmetaobject = baseobject._getClass(); - methods = classmetaobject.getReflectiveMethods(); - - // call _setMetaobject() after the metaobject is settled. - baseobject._setMetaobject(this); - } - - /** - * Returns the name of the method specified - * by identifier. - */ - public final String getMethodName(int identifier) { - String mname = methods[identifier].getName(); - int j = ClassMetaobject.methodPrefixLen; - for (;;) { - char c = mname.charAt(j++); - if (c < '0' || '9' < c) - break; - } - - return mname.substring(j); - } - - /** - * Returns an array of Class objects representing the - * formal parameter types of the method specified - * by identifier. - */ - public final Class[] getParameterTypes(int identifier) { - return methods[identifier].getParameterTypes(); - } - - /** - * Returns a Class objects representing the - * return type of the method specified by identifier. - */ - public final Class getReturnType(int identifier) { - return methods[identifier].getReturnType(); - } - - /** - * Is invoked when public fields of the base-level - * class are read and the runtime system intercepts it. - * This method simply returns the value of the field. - * - *

Every subclass of this class should redefine this method. - */ - public Object trapFieldRead(String name) { - Class jc = getClassMetaobject().getJavaClass(); - try { - return jc.getField(name).get(getObject()); - } - catch (NoSuchFieldException e) { - throw new RuntimeException(e.toString()); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Is invoked when public fields of the base-level - * class are modified and the runtime system intercepts it. - * This method simply sets the field to the given value. - * - *

Every subclass of this class should redefine this method. - */ - public void trapFieldWrite(String name, Object value) { - Class jc = getClassMetaobject().getJavaClass(); - try { - jc.getField(name).set(getObject(), value); - } - catch (NoSuchFieldException e) { - throw new RuntimeException(e.toString()); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Is invoked when base-level method invocation is intercepted. - * This method simply executes the intercepted method invocation - * with the original parameters and returns the resulting value. - * - *

Every subclass of this class should redefine this method. - * - *

Note: this method is not invoked if the base-level method - * is invoked by a constructor in the super class. For example, - * - *

-     * abstract class A {
-     *   abstract void initialize();
-     *   A() {
-     *       initialize();    // not intercepted
-     *   }
-     * }
-     *
-     * class B extends A {
-     *   void initialize() { System.out.println("initialize()"); }
-     *   B() {
-     *       super();
-     *       initialize();    // intercepted
-     *   }
-     * }
- * - *

if an instance of B is created, - * the invocation of initialize() in B is intercepted only once. - * The first invocation by the constructor in A is not intercepted. - * This is because the link between a base-level object and a - * metaobject is not created until the execution of a - * constructor of the super class finishes. - */ - public Object trapMethodcall(int identifier, Object[] args) - throws Throwable - { - try { - return methods[identifier].invoke(getObject(), args); - } - catch (java.lang.reflect.InvocationTargetException e) { - throw e.getTargetException(); - } - catch (java.lang.IllegalAccessException e) { - throw new CannotInvokeException(e); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/Reflection.java b/src/com/wenshuo/agent/javassist/tools/reflect/Reflection.java deleted file mode 100644 index 4e2219f..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/Reflection.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -import java.util.Iterator; -import com.wenshuo.agent.javassist.*; -import com.wenshuo.agent.javassist.CtMethod.ConstParameter; -import com.wenshuo.agent.javassist.bytecode.ClassFile; -import com.wenshuo.agent.javassist.bytecode.BadBytecode; -import com.wenshuo.agent.javassist.bytecode.MethodInfo; - -/** - * The class implementing the behavioral reflection mechanism. - * - *

If a class is reflective, - * then all the method invocations on every - * instance of that class are intercepted by the runtime - * metaobject controlling that instance. The methods inherited from the - * super classes are also intercepted except final methods. To intercept - * a final method in a super class, that super class must be also reflective. - * - *

To do this, the original class file representing a reflective class: - * - *

- * class Person {
- *   public int f(int i) { return i + 1; }
- *   public int value;
- * }
- * 
- * - *

is modified so that it represents a class: - * - *

- * class Person implements Metalevel {
- *   public int _original_f(int i) { return i + 1; }
- *   public int f(int i) { delegate to the metaobject }
- *
- *   public int value;
- *   public int _r_value() { read "value" }
- *   public void _w_value(int v) { write "value" }
- *
- *   public ClassMetaobject _getClass() { return a class metaobject }
- *   public Metaobject _getMetaobject() { return a metaobject }
- *   public void _setMetaobject(Metaobject m) { change a metaobject }
- * }
- * 
- * - * @see javassist.tools.reflect.ClassMetaobject - * @see javassist.tools.reflect.Metaobject - * @see javassist.tools.reflect.Loader - * @see javassist.tools.reflect.Compiler - */ -public class Reflection implements Translator { - - static final String classobjectField = "_classobject"; - static final String classobjectAccessor = "_getClass"; - static final String metaobjectField = "_metaobject"; - static final String metaobjectGetter = "_getMetaobject"; - static final String metaobjectSetter = "_setMetaobject"; - static final String readPrefix = "_r_"; - static final String writePrefix = "_w_"; - - static final String metaobjectClassName = "javassist.tools.reflect.Metaobject"; - static final String classMetaobjectClassName - = "javassist.tools.reflect.ClassMetaobject"; - - protected CtMethod trapMethod, trapStaticMethod; - protected CtMethod trapRead, trapWrite; - protected CtClass[] readParam; - - protected ClassPool classPool; - protected CodeConverter converter; - - private boolean isExcluded(String name) { - return name.startsWith(ClassMetaobject.methodPrefix) - || name.equals(classobjectAccessor) - || name.equals(metaobjectSetter) - || name.equals(metaobjectGetter) - || name.startsWith(readPrefix) - || name.startsWith(writePrefix); - } - - /** - * Constructs a new Reflection object. - */ - public Reflection() { - classPool = null; - converter = new CodeConverter(); - } - - /** - * Initializes the object. - */ - public void start(ClassPool pool) throws NotFoundException { - classPool = pool; - final String msg - = "javassist.tools.reflect.Sample is not found or broken."; - try { - CtClass c = classPool.get("javassist.tools.reflect.Sample"); - rebuildClassFile(c.getClassFile()); - trapMethod = c.getDeclaredMethod("trap"); - trapStaticMethod = c.getDeclaredMethod("trapStatic"); - trapRead = c.getDeclaredMethod("trapRead"); - trapWrite = c.getDeclaredMethod("trapWrite"); - readParam - = new CtClass[] { classPool.get("java.lang.Object") }; - } - catch (NotFoundException e) { - throw new RuntimeException(msg); - } catch (BadBytecode e) { - throw new RuntimeException(msg); - } - } - - /** - * Inserts hooks for intercepting accesses to the fields declared - * in reflective classes. - */ - public void onLoad(ClassPool pool, String classname) - throws CannotCompileException, NotFoundException - { - CtClass clazz = pool.get(classname); - clazz.instrument(converter); - } - - /** - * Produces a reflective class. - * If the super class is also made reflective, it must be done - * before the sub class. - * - * @param classname the name of the reflective class - * @param metaobject the class name of metaobjects. - * @param metaclass the class name of the class metaobject. - * @return false if the class is already reflective. - * - * @see javassist.tools.reflect.Metaobject - * @see javassist.tools.reflect.ClassMetaobject - */ - public boolean makeReflective(String classname, - String metaobject, String metaclass) - throws CannotCompileException, NotFoundException - { - return makeReflective(classPool.get(classname), - classPool.get(metaobject), - classPool.get(metaclass)); - } - - /** - * Produces a reflective class. - * If the super class is also made reflective, it must be done - * before the sub class. - * - * @param clazz the reflective class. - * @param metaobject the class of metaobjects. - * It must be a subclass of - * Metaobject. - * @param metaclass the class of the class metaobject. - * It must be a subclass of - * ClassMetaobject. - * @return false if the class is already reflective. - * - * @see javassist.tools.reflect.Metaobject - * @see javassist.tools.reflect.ClassMetaobject - */ - public boolean makeReflective(Class clazz, - Class metaobject, Class metaclass) - throws CannotCompileException, NotFoundException - { - return makeReflective(clazz.getName(), metaobject.getName(), - metaclass.getName()); - } - - /** - * Produces a reflective class. It modifies the given - * CtClass object and makes it reflective. - * If the super class is also made reflective, it must be done - * before the sub class. - * - * @param clazz the reflective class. - * @param metaobject the class of metaobjects. - * It must be a subclass of - * Metaobject. - * @param metaclass the class of the class metaobject. - * It must be a subclass of - * ClassMetaobject. - * @return false if the class is already reflective. - * - * @see javassist.tools.reflect.Metaobject - * @see javassist.tools.reflect.ClassMetaobject - */ - public boolean makeReflective(CtClass clazz, - CtClass metaobject, CtClass metaclass) - throws CannotCompileException, CannotReflectException, - NotFoundException - { - if (clazz.isInterface()) - throw new CannotReflectException( - "Cannot reflect an interface: " + clazz.getName()); - - if (clazz.subclassOf(classPool.get(classMetaobjectClassName))) - throw new CannotReflectException( - "Cannot reflect a subclass of ClassMetaobject: " - + clazz.getName()); - - if (clazz.subclassOf(classPool.get(metaobjectClassName))) - throw new CannotReflectException( - "Cannot reflect a subclass of Metaobject: " - + clazz.getName()); - - registerReflectiveClass(clazz); - return modifyClassfile(clazz, metaobject, metaclass); - } - - /** - * Registers a reflective class. The field accesses to the instances - * of this class are instrumented. - */ - private void registerReflectiveClass(CtClass clazz) { - CtField[] fs = clazz.getDeclaredFields(); - for (int i = 0; i < fs.length; ++i) { - CtField f = fs[i]; - int mod = f.getModifiers(); - if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { - String name = f.getName(); - converter.replaceFieldRead(f, clazz, readPrefix + name); - converter.replaceFieldWrite(f, clazz, writePrefix + name); - } - } - } - - private boolean modifyClassfile(CtClass clazz, CtClass metaobject, - CtClass metaclass) - throws CannotCompileException, NotFoundException - { - if (clazz.getAttribute("Reflective") != null) - return false; // this is already reflective. - else - clazz.setAttribute("Reflective", new byte[0]); - - CtClass mlevel = classPool.get("javassist.tools.reflect.Metalevel"); - boolean addMeta = !clazz.subtypeOf(mlevel); - if (addMeta) - clazz.addInterface(mlevel); - - processMethods(clazz, addMeta); - processFields(clazz); - - CtField f; - if (addMeta) { - f = new CtField(classPool.get("javassist.tools.reflect.Metaobject"), - metaobjectField, clazz); - f.setModifiers(Modifier.PROTECTED); - clazz.addField(f, CtField.Initializer.byNewWithParams(metaobject)); - - clazz.addMethod(CtNewMethod.getter(metaobjectGetter, f)); - clazz.addMethod(CtNewMethod.setter(metaobjectSetter, f)); - } - - f = new CtField(classPool.get("javassist.tools.reflect.ClassMetaobject"), - classobjectField, clazz); - f.setModifiers(Modifier.PRIVATE | Modifier.STATIC); - clazz.addField(f, CtField.Initializer.byNew(metaclass, - new String[] { clazz.getName() })); - - clazz.addMethod(CtNewMethod.getter(classobjectAccessor, f)); - return true; - } - - private void processMethods(CtClass clazz, boolean dontSearch) - throws CannotCompileException, NotFoundException - { - CtMethod[] ms = clazz.getMethods(); - for (int i = 0; i < ms.length; ++i) { - CtMethod m = ms[i]; - int mod = m.getModifiers(); - if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod)) - processMethods0(mod, clazz, m, i, dontSearch); - } - } - - private void processMethods0(int mod, CtClass clazz, - CtMethod m, int identifier, boolean dontSearch) - throws CannotCompileException, NotFoundException - { - CtMethod body; - String name = m.getName(); - - if (isExcluded(name)) // internally-used method inherited - return; // from a reflective class. - - CtMethod m2; - if (m.getDeclaringClass() == clazz) { - if (Modifier.isNative(mod)) - return; - - m2 = m; - if (Modifier.isFinal(mod)) { - mod &= ~Modifier.FINAL; - m2.setModifiers(mod); - } - } - else { - if (Modifier.isFinal(mod)) - return; - - mod &= ~Modifier.NATIVE; - m2 = CtNewMethod.delegator(findOriginal(m, dontSearch), clazz); - m2.setModifiers(mod); - clazz.addMethod(m2); - } - - m2.setName(ClassMetaobject.methodPrefix + identifier - + "_" + name); - - if (Modifier.isStatic(mod)) - body = trapStaticMethod; - else - body = trapMethod; - - CtMethod wmethod - = CtNewMethod.wrapped(m.getReturnType(), name, - m.getParameterTypes(), m.getExceptionTypes(), - body, ConstParameter.integer(identifier), - clazz); - wmethod.setModifiers(mod); - clazz.addMethod(wmethod); - } - - private CtMethod findOriginal(CtMethod m, boolean dontSearch) - throws NotFoundException - { - if (dontSearch) - return m; - - String name = m.getName(); - CtMethod[] ms = m.getDeclaringClass().getDeclaredMethods(); - for (int i = 0; i < ms.length; ++i) { - String orgName = ms[i].getName(); - if (orgName.endsWith(name) - && orgName.startsWith(ClassMetaobject.methodPrefix) - && ms[i].getSignature().equals(m.getSignature())) - return ms[i]; - } - - return m; - } - - private void processFields(CtClass clazz) - throws CannotCompileException, NotFoundException - { - CtField[] fs = clazz.getDeclaredFields(); - for (int i = 0; i < fs.length; ++i) { - CtField f = fs[i]; - int mod = f.getModifiers(); - if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { - mod |= Modifier.STATIC; - String name = f.getName(); - CtClass ftype = f.getType(); - CtMethod wmethod - = CtNewMethod.wrapped(ftype, readPrefix + name, - readParam, null, trapRead, - ConstParameter.string(name), - clazz); - wmethod.setModifiers(mod); - clazz.addMethod(wmethod); - CtClass[] writeParam = new CtClass[2]; - writeParam[0] = classPool.get("java.lang.Object"); - writeParam[1] = ftype; - wmethod = CtNewMethod.wrapped(CtClass.voidType, - writePrefix + name, - writeParam, null, trapWrite, - ConstParameter.string(name), clazz); - wmethod.setModifiers(mod); - clazz.addMethod(wmethod); - } - } - } - - public void rebuildClassFile(ClassFile cf) throws BadBytecode { - if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_6) - return; - - Iterator methods = cf.getMethods().iterator(); - while (methods.hasNext()) { - MethodInfo mi = (MethodInfo)methods.next(); - mi.rebuildStackMap(classPool); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/Sample.java b/src/com/wenshuo/agent/javassist/tools/reflect/Sample.java deleted file mode 100644 index 9622be5..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/Sample.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.reflect; - -/** - * A template used for defining a reflective class. - */ -public class Sample { - private Metaobject _metaobject; - private static ClassMetaobject _classobject; - - public Object trap(Object[] args, int identifier) throws Throwable { - Metaobject mobj; - mobj = _metaobject; - if (mobj == null) - return ClassMetaobject.invoke(this, identifier, args); - else - return mobj.trapMethodcall(identifier, args); - } - - public static Object trapStatic(Object[] args, int identifier) - throws Throwable - { - return _classobject.trapMethodcall(identifier, args); - } - - public static Object trapRead(Object[] args, String name) { - if (args[0] == null) - return _classobject.trapFieldRead(name); - else - return ((Metalevel)args[0])._getMetaobject().trapFieldRead(name); - } - - public static Object trapWrite(Object[] args, String name) { - Metalevel base = (Metalevel)args[0]; - if (base == null) - _classobject.trapFieldWrite(name, args[1]); - else - base._getMetaobject().trapFieldWrite(name, args[1]); - - return null; - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/reflect/package.html b/src/com/wenshuo/agent/javassist/tools/reflect/package.html deleted file mode 100644 index 10a4196..0000000 --- a/src/com/wenshuo/agent/javassist/tools/reflect/package.html +++ /dev/null @@ -1,35 +0,0 @@ - - -Runtime Behavioral Reflection. - -

(also recently known as interceptors or AOP?) - -

This package enables a metaobject to trap method calls and field -accesses on a regular Java object. It provides a class -Reflection, which is a main module for implementing -runtime behavioral reflection. -It also provides -a class Loader and Compiler -as utilities for dynamically or statically -translating a regular class into a reflective class. - -

An instance of the reflective class is associated with -a runtime metaobject and a runtime class metaobject, which control -the behavior of that instance. -The runtime -metaobject is created for every (base-level) instance but the -runtime class metaobject is created for every (base-level) class. -Metaobject is the root class of the runtime -metaobject and ClassMetaobject is the root class -of the runtime class metaobject. - -

This package is provided as a sample implementation of the -reflection mechanism with Javassist. All the programs in this package -uses only the regular Javassist API; they never call any hidden -methods. - -

The most significant class in this package is Reflection. -See the description of this class first. - - - diff --git a/src/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java b/src/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java deleted file mode 100644 index 4ffcc7a..0000000 --- a/src/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.rmi; - -import java.io.*; - -import com.wenshuo.agent.javassist.tools.web.*; -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.ClassPool; -import java.lang.reflect.Method; -import java.util.Hashtable; -import java.util.Vector; - -/** - * An AppletServer object is a web server that an ObjectImporter - * communicates with. It makes the objects specified by - * exportObject() remotely accessible from applets. - * If the classes of the exported objects are requested by the client-side - * JVM, this web server sends proxy classes for the requested classes. - * - * @see javassist.tools.rmi.ObjectImporter - */ -public class AppletServer extends Webserver { - private StubGenerator stubGen; - private Hashtable exportedNames; - private Vector exportedObjects; - - private static final byte[] okHeader - = "HTTP/1.0 200 OK\r\n\r\n".getBytes(); - - /** - * Constructs a web server. - * - * @param port port number - */ - public AppletServer(String port) - throws IOException, NotFoundException, CannotCompileException - { - this(Integer.parseInt(port)); - } - - /** - * Constructs a web server. - * - * @param port port number - */ - public AppletServer(int port) - throws IOException, NotFoundException, CannotCompileException - { - this(ClassPool.getDefault(), new StubGenerator(), port); - } - - /** - * Constructs a web server. - * - * @param port port number - * @param src the source of classs files. - */ - public AppletServer(int port, ClassPool src) - throws IOException, NotFoundException, CannotCompileException - { - this(new ClassPool(src), new StubGenerator(), port); - } - - private AppletServer(ClassPool loader, StubGenerator gen, int port) - throws IOException, NotFoundException, CannotCompileException - { - super(port); - exportedNames = new Hashtable(); - exportedObjects = new Vector(); - stubGen = gen; - addTranslator(loader, gen); - } - - /** - * Begins the HTTP service. - */ - public void run() { - super.run(); - } - - /** - * Exports an object. - * This method produces the bytecode of the proxy class used - * to access the exported object. A remote applet can load - * the proxy class and call a method on the exported object. - * - * @param name the name used for looking the object up. - * @param obj the exported object. - * @return the object identifier - * - * @see javassist.tools.rmi.ObjectImporter#lookupObject(String) - */ - public synchronized int exportObject(String name, Object obj) - throws CannotCompileException - { - Class clazz = obj.getClass(); - ExportedObject eo = new ExportedObject(); - eo.object = obj; - eo.methods = clazz.getMethods(); - exportedObjects.addElement(eo); - eo.identifier = exportedObjects.size() - 1; - if (name != null) - exportedNames.put(name, eo); - - try { - stubGen.makeProxyClass(clazz); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - - return eo.identifier; - } - - /** - * Processes a request from a web browser (an ObjectImporter). - */ - public void doReply(InputStream in, OutputStream out, String cmd) - throws IOException, BadHttpRequest - { - if (cmd.startsWith("POST /rmi ")) - processRMI(in, out); - else if (cmd.startsWith("POST /lookup ")) - lookupName(cmd, in, out); - else - super.doReply(in, out, cmd); - } - - private void processRMI(InputStream ins, OutputStream outs) - throws IOException - { - ObjectInputStream in = new ObjectInputStream(ins); - - int objectId = in.readInt(); - int methodId = in.readInt(); - Exception err = null; - Object rvalue = null; - try { - ExportedObject eo - = (ExportedObject)exportedObjects.elementAt(objectId); - Object[] args = readParameters(in); - rvalue = convertRvalue(eo.methods[methodId].invoke(eo.object, - args)); - } - catch(Exception e) { - err = e; - logging2(e.toString()); - } - - outs.write(okHeader); - ObjectOutputStream out = new ObjectOutputStream(outs); - if (err != null) { - out.writeBoolean(false); - out.writeUTF(err.toString()); - } - else - try { - out.writeBoolean(true); - out.writeObject(rvalue); - } - catch (NotSerializableException e) { - logging2(e.toString()); - } - catch (InvalidClassException e) { - logging2(e.toString()); - } - - out.flush(); - out.close(); - in.close(); - } - - private Object[] readParameters(ObjectInputStream in) - throws IOException, ClassNotFoundException - { - int n = in.readInt(); - Object[] args = new Object[n]; - for (int i = 0; i < n; ++i) { - Object a = in.readObject(); - if (a instanceof RemoteRef) { - RemoteRef ref = (RemoteRef)a; - ExportedObject eo - = (ExportedObject)exportedObjects.elementAt(ref.oid); - a = eo.object; - } - - args[i] = a; - } - - return args; - } - - private Object convertRvalue(Object rvalue) - throws CannotCompileException - { - if (rvalue == null) - return null; // the return type is void. - - String classname = rvalue.getClass().getName(); - if (stubGen.isProxyClass(classname)) - return new RemoteRef(exportObject(null, rvalue), classname); - else - return rvalue; - } - - private void lookupName(String cmd, InputStream ins, OutputStream outs) - throws IOException - { - ObjectInputStream in = new ObjectInputStream(ins); - String name = DataInputStream.readUTF(in); - ExportedObject found = (ExportedObject)exportedNames.get(name); - outs.write(okHeader); - ObjectOutputStream out = new ObjectOutputStream(outs); - if (found == null) { - logging2(name + "not found."); - out.writeInt(-1); // error code - out.writeUTF("error"); - } - else { - logging2(name); - out.writeInt(found.identifier); - out.writeUTF(found.object.getClass().getName()); - } - - out.flush(); - out.close(); - in.close(); - } -} - -class ExportedObject { - public int identifier; - public Object object; - public Method[] methods; -} diff --git a/src/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java b/src/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java deleted file mode 100644 index 776160e..0000000 --- a/src/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.rmi; - -import java.io.*; -import java.net.*; -import java.applet.Applet; -import java.lang.reflect.*; - -/** - * The object importer enables applets to call a method on a remote - * object running on the Webserver (the main class of this - * package). - * - *

To access the remote - * object, the applet first calls lookupObject() and - * obtains a proxy object, which is a reference to that object. - * The class name of the proxy object is identical to that of - * the remote object. - * The proxy object provides the same set of methods as the remote object. - * If one of the methods is invoked on the proxy object, - * the invocation is delegated to the remote object. - * From the viewpoint of the applet, therefore, the two objects are - * identical. The applet can access the object on the server - * with the regular Java syntax without concern about the actual - * location. - * - *

The methods remotely called by the applet must be public. - * This is true even if the applet's class and the remote object's classs - * belong to the same package. - * - *

If class X is a class of remote objects, a subclass of X must be - * also a class of remote objects. On the other hand, this restriction - * is not applied to the superclass of X. The class X does not have to - * contain a constructor taking no arguments. - * - *

The parameters to a remote method is passed in the call-by-value - * manner. Thus all the parameter classes must implement - * java.io.Serializable. However, if the parameter is the - * proxy object, the reference to the remote object instead of a copy of - * the object is passed to the method. - * - *

Because of the limitations of the current implementation, - *

    - *
  • The parameter objects cannot contain the proxy - * object as a field value. - *
  • If class C is of the remote object, then - * the applet cannot instantiate C locally or remotely. - *
- * - *

All the exceptions thrown by the remote object are converted - * into RemoteException. Since this exception is a subclass - * of RuntimeException, the caller method does not need - * to catch the exception. However, good programs should catch - * the RuntimeException. - * - * @see javassist.tools.rmi.AppletServer - * @see javassist.tools.rmi.RemoteException - * @see javassist.tools.web.Viewer - */ -public class ObjectImporter implements java.io.Serializable { - private final byte[] endofline = { 0x0d, 0x0a }; - private String servername, orgServername; - private int port, orgPort; - - protected byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes(); - protected byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes(); - - /** - * Constructs an object importer. - * - *

Remote objects are imported from the web server that the given - * applet has been loaded from. - * - * @param applet the applet loaded from the Webserver. - */ - public ObjectImporter(Applet applet) { - URL codebase = applet.getCodeBase(); - orgServername = servername = codebase.getHost(); - orgPort = port = codebase.getPort(); - } - - /** - * Constructs an object importer. - * - *

If you run a program with javassist.tools.web.Viewer, - * you can construct an object importer as follows: - * - *

-     * Viewer v = (Viewer)this.getClass().getClassLoader();
-     * ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort());
-     * 
- * - * @see javassist.tools.web.Viewer - */ - public ObjectImporter(String servername, int port) { - this.orgServername = this.servername = servername; - this.orgPort = this.port = port; - } - - /** - * Finds the object exported by a server with the specified name. - * If the object is not found, this method returns null. - * - * @param name the name of the exported object. - * @return the proxy object or null. - */ - public Object getObject(String name) { - try { - return lookupObject(name); - } - catch (ObjectNotFoundException e) { - return null; - } - } - - /** - * Sets an http proxy server. After this method is called, the object - * importer connects a server through the http proxy server. - */ - public void setHttpProxy(String host, int port) { - String proxyHeader = "POST http://" + orgServername + ":" + orgPort; - String cmd = proxyHeader + "/lookup HTTP/1.0"; - lookupCommand = cmd.getBytes(); - cmd = proxyHeader + "/rmi HTTP/1.0"; - rmiCommand = cmd.getBytes(); - this.servername = host; - this.port = port; - } - - /** - * Finds the object exported by the server with the specified name. - * It sends a POST request to the server (via an http proxy server - * if needed). - * - * @param name the name of the exported object. - * @return the proxy object. - */ - public Object lookupObject(String name) throws ObjectNotFoundException - { - try { - Socket sock = new Socket(servername, port); - OutputStream out = sock.getOutputStream(); - out.write(lookupCommand); - out.write(endofline); - out.write(endofline); - - ObjectOutputStream dout = new ObjectOutputStream(out); - dout.writeUTF(name); - dout.flush(); - - InputStream in = new BufferedInputStream(sock.getInputStream()); - skipHeader(in); - ObjectInputStream din = new ObjectInputStream(in); - int n = din.readInt(); - String classname = din.readUTF(); - din.close(); - dout.close(); - sock.close(); - - if (n >= 0) - return createProxy(n, classname); - } - catch (Exception e) { - e.printStackTrace(); - throw new ObjectNotFoundException(name, e); - } - - throw new ObjectNotFoundException(name); - } - - private static final Class[] proxyConstructorParamTypes - = new Class[] { ObjectImporter.class, int.class }; - - private Object createProxy(int oid, String classname) throws Exception { - Class c = Class.forName(classname); - Constructor cons = c.getConstructor(proxyConstructorParamTypes); - return cons.newInstance(new Object[] { this, new Integer(oid) }); - } - - /** - * Calls a method on a remote object. - * It sends a POST request to the server (via an http proxy server - * if needed). - * - *

This method is called by only proxy objects. - */ - public Object call(int objectid, int methodid, Object[] args) - throws RemoteException - { - boolean result; - Object rvalue; - String errmsg; - - try { - /* This method establishes a raw tcp connection for sending - * a POST message. Thus the object cannot communicate a - * remote object beyond a fire wall. To avoid this problem, - * the connection should be established with a mechanism - * collaborating a proxy server. Unfortunately, java.lang.URL - * does not seem to provide such a mechanism. - * - * You might think that using HttpURLConnection is a better - * way than constructing a raw tcp connection. Unfortunately, - * URL.openConnection() does not return an HttpURLConnection - * object in Netscape's JVM. It returns a - * netscape.net.URLConnection object. - * - * lookupObject() has the same problem. - */ - Socket sock = new Socket(servername, port); - OutputStream out = new BufferedOutputStream( - sock.getOutputStream()); - out.write(rmiCommand); - out.write(endofline); - out.write(endofline); - - ObjectOutputStream dout = new ObjectOutputStream(out); - dout.writeInt(objectid); - dout.writeInt(methodid); - writeParameters(dout, args); - dout.flush(); - - InputStream ins = new BufferedInputStream(sock.getInputStream()); - skipHeader(ins); - ObjectInputStream din = new ObjectInputStream(ins); - result = din.readBoolean(); - rvalue = null; - errmsg = null; - if (result) - rvalue = din.readObject(); - else - errmsg = din.readUTF(); - - din.close(); - dout.close(); - sock.close(); - - if (rvalue instanceof RemoteRef) { - RemoteRef ref = (RemoteRef)rvalue; - rvalue = createProxy(ref.oid, ref.classname); - } - } - catch (ClassNotFoundException e) { - throw new RemoteException(e); - } - catch (IOException e) { - throw new RemoteException(e); - } - catch (Exception e) { - throw new RemoteException(e); - } - - if (result) - return rvalue; - else - throw new RemoteException(errmsg); - } - - private void skipHeader(InputStream in) throws IOException { - int len; - do { - int c; - len = 0; - while ((c = in.read()) >= 0 && c != 0x0d) - ++len; - - in.read(); /* skip 0x0a (LF) */ - } while (len > 0); - } - - private void writeParameters(ObjectOutputStream dout, Object[] params) - throws IOException - { - int n = params.length; - dout.writeInt(n); - for (int i = 0; i < n; ++i) - if (params[i] instanceof Proxy) { - Proxy p = (Proxy)params[i]; - dout.writeObject(new RemoteRef(p._getObjectId())); - } - else - dout.writeObject(params[i]); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java b/src/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java deleted file mode 100644 index cc4f801..0000000 --- a/src/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.rmi; - -public class ObjectNotFoundException extends Exception { - public ObjectNotFoundException(String name) { - super(name + " is not exported"); - } - - public ObjectNotFoundException(String name, Exception e) { - super(name + " because of " + e.toString()); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/rmi/Proxy.java b/src/com/wenshuo/agent/javassist/tools/rmi/Proxy.java deleted file mode 100644 index 9be28be..0000000 --- a/src/com/wenshuo/agent/javassist/tools/rmi/Proxy.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.rmi; - -/** - * An interface implemented by proxy classes. - * - * @see javassist.tools.rmi.StubGenerator - */ -public interface Proxy { - int _getObjectId(); -} diff --git a/src/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java b/src/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java deleted file mode 100644 index 9d6a3c7..0000000 --- a/src/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.rmi; - -/** - * RemoteException represents any exception thrown - * during remote method invocation. - */ -public class RemoteException extends RuntimeException { - public RemoteException(String msg) { - super(msg); - } - - public RemoteException(Exception e) { - super("by " + e.toString()); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java b/src/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java deleted file mode 100644 index a35a417..0000000 --- a/src/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.rmi; - -/** - * Remote reference. This class is internally used for sending a remote - * reference through a network stream. - */ -public class RemoteRef implements java.io.Serializable { - public int oid; - public String classname; - - public RemoteRef(int i) { - oid = i; - classname = null; - } - - public RemoteRef(int i, String name) { - oid = i; - classname = name; - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/rmi/Sample.java b/src/com/wenshuo/agent/javassist/tools/rmi/Sample.java deleted file mode 100644 index 409e959..0000000 --- a/src/com/wenshuo/agent/javassist/tools/rmi/Sample.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.rmi; - -/** - * A template used for defining a proxy class. - * The class file of this class is read by the StubGenerator - * class. - */ -public class Sample { - private ObjectImporter importer; - private int objectId; - - public Object forward(Object[] args, int identifier) { - return importer.call(objectId, identifier, args); - } - - public static Object forwardStatic(Object[] args, int identifier) - throws RemoteException - { - throw new RemoteException("cannot call a static method."); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java b/src/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java deleted file mode 100644 index 9eaf6bf..0000000 --- a/src/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.rmi; - -import com.wenshuo.agent.javassist.*; -import java.lang.reflect.Method; -import java.util.Hashtable; -import com.wenshuo.agent.javassist.CtMethod.ConstParameter; - -/** - * A stub-code generator. It is used for producing a proxy class. - * - *

The proxy class for class A is as follows: - * - *

public class A implements Proxy, Serializable {
- *   private ObjectImporter importer;
- *   private int objectId;
- *   public int _getObjectId() { return objectId; }
- *   public A(ObjectImporter oi, int id) {
- *     importer = oi; objectId = id;
- *   }
- *
- *   ... the same methods that the original class A declares ...
- * }
- * - *

Instances of the proxy class is created by an - * ObjectImporter object. - */ -public class StubGenerator implements Translator { - private static final String fieldImporter = "importer"; - private static final String fieldObjectId = "objectId"; - private static final String accessorObjectId = "_getObjectId"; - private static final String sampleClass = "javassist.tools.rmi.Sample"; - - private ClassPool classPool; - private Hashtable proxyClasses; - private CtMethod forwardMethod; - private CtMethod forwardStaticMethod; - - private CtClass[] proxyConstructorParamTypes; - private CtClass[] interfacesForProxy; - private CtClass[] exceptionForProxy; - - /** - * Constructs a stub-code generator. - */ - public StubGenerator() { - proxyClasses = new Hashtable(); - } - - /** - * Initializes the object. - * This is a method declared in javassist.Translator. - * - * @see javassist.Translator#start(ClassPool) - */ - public void start(ClassPool pool) throws NotFoundException { - classPool = pool; - CtClass c = pool.get(sampleClass); - forwardMethod = c.getDeclaredMethod("forward"); - forwardStaticMethod = c.getDeclaredMethod("forwardStatic"); - - proxyConstructorParamTypes - = pool.get(new String[] { "javassist.tools.rmi.ObjectImporter", - "int" }); - interfacesForProxy - = pool.get(new String[] { "java.io.Serializable", - "javassist.tools.rmi.Proxy" }); - exceptionForProxy - = new CtClass[] { pool.get("javassist.tools.rmi.RemoteException") }; - } - - /** - * Does nothing. - * This is a method declared in javassist.Translator. - * @see javassist.Translator#onLoad(ClassPool,String) - */ - public void onLoad(ClassPool pool, String classname) {} - - /** - * Returns true if the specified class is a proxy class - * recorded by makeProxyClass(). - * - * @param name a fully-qualified class name - */ - public boolean isProxyClass(String name) { - return proxyClasses.get(name) != null; - } - - /** - * Makes a proxy class. The produced class is substituted - * for the original class. - * - * @param clazz the class referenced - * through the proxy class. - * @return false if the proxy class - * has been already produced. - */ - public synchronized boolean makeProxyClass(Class clazz) - throws CannotCompileException, NotFoundException - { - String classname = clazz.getName(); - if (proxyClasses.get(classname) != null) - return false; - else { - CtClass ctclazz = produceProxyClass(classPool.get(classname), - clazz); - proxyClasses.put(classname, ctclazz); - modifySuperclass(ctclazz); - return true; - } - } - - private CtClass produceProxyClass(CtClass orgclass, Class orgRtClass) - throws CannotCompileException, NotFoundException - { - int modify = orgclass.getModifiers(); - if (Modifier.isAbstract(modify) || Modifier.isNative(modify) - || !Modifier.isPublic(modify)) - throw new CannotCompileException(orgclass.getName() - + " must be public, non-native, and non-abstract."); - - CtClass proxy = classPool.makeClass(orgclass.getName(), - orgclass.getSuperclass()); - - proxy.setInterfaces(interfacesForProxy); - - CtField f - = new CtField(classPool.get("javassist.tools.rmi.ObjectImporter"), - fieldImporter, proxy); - f.setModifiers(Modifier.PRIVATE); - proxy.addField(f, CtField.Initializer.byParameter(0)); - - f = new CtField(CtClass.intType, fieldObjectId, proxy); - f.setModifiers(Modifier.PRIVATE); - proxy.addField(f, CtField.Initializer.byParameter(1)); - - proxy.addMethod(CtNewMethod.getter(accessorObjectId, f)); - - proxy.addConstructor(CtNewConstructor.defaultConstructor(proxy)); - CtConstructor cons - = CtNewConstructor.skeleton(proxyConstructorParamTypes, - null, proxy); - proxy.addConstructor(cons); - - try { - addMethods(proxy, orgRtClass.getMethods()); - return proxy; - } - catch (SecurityException e) { - throw new CannotCompileException(e); - } - } - - private CtClass toCtClass(Class rtclass) throws NotFoundException { - String name; - if (!rtclass.isArray()) - name = rtclass.getName(); - else { - StringBuffer sbuf = new StringBuffer(); - do { - sbuf.append("[]"); - rtclass = rtclass.getComponentType(); - } while(rtclass.isArray()); - sbuf.insert(0, rtclass.getName()); - name = sbuf.toString(); - } - - return classPool.get(name); - } - - private CtClass[] toCtClass(Class[] rtclasses) throws NotFoundException { - int n = rtclasses.length; - CtClass[] ctclasses = new CtClass[n]; - for (int i = 0; i < n; ++i) - ctclasses[i] = toCtClass(rtclasses[i]); - - return ctclasses; - } - - /* ms must not be an array of CtMethod. To invoke a method ms[i] - * on a server, a client must send i to the server. - */ - private void addMethods(CtClass proxy, Method[] ms) - throws CannotCompileException, NotFoundException - { - CtMethod wmethod; - for (int i = 0; i < ms.length; ++i) { - Method m = ms[i]; - int mod = m.getModifiers(); - if (m.getDeclaringClass() != Object.class - && !Modifier.isFinal(mod)) - if (Modifier.isPublic(mod)) { - CtMethod body; - if (Modifier.isStatic(mod)) - body = forwardStaticMethod; - else - body = forwardMethod; - - wmethod - = CtNewMethod.wrapped(toCtClass(m.getReturnType()), - m.getName(), - toCtClass(m.getParameterTypes()), - exceptionForProxy, - body, - ConstParameter.integer(i), - proxy); - wmethod.setModifiers(mod); - proxy.addMethod(wmethod); - } - else if (!Modifier.isProtected(mod) - && !Modifier.isPrivate(mod)) - // if package method - throw new CannotCompileException( - "the methods must be public, protected, or private."); - } - } - - /** - * Adds a default constructor to the super classes. - */ - private void modifySuperclass(CtClass orgclass) - throws CannotCompileException, NotFoundException - { - CtClass superclazz; - for (;; orgclass = superclazz) { - superclazz = orgclass.getSuperclass(); - if (superclazz == null) - break; - - try { - superclazz.getDeclaredConstructor(null); - break; // the constructor with no arguments is found. - } - catch (NotFoundException e) { - } - - superclazz.addConstructor( - CtNewConstructor.defaultConstructor(superclazz)); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/rmi/package.html b/src/com/wenshuo/agent/javassist/tools/rmi/package.html deleted file mode 100644 index 5432a94..0000000 --- a/src/com/wenshuo/agent/javassist/tools/rmi/package.html +++ /dev/null @@ -1,16 +0,0 @@ - - -Sample implementation of remote method invocation. - -

This package enables applets to access remote objects -running on the web server with regular Java syntax. -It is provided as a sample implementation with Javassist. -All the programs in this package uses only the regular -Javassist API; they never call any hidden methods. - -

The most significant class of this package is -ObjectImporter. -See the description of this class first. - - - diff --git a/src/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java b/src/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java deleted file mode 100644 index 93cf67c..0000000 --- a/src/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.web; - -/** - * Thrown when receiving an invalid HTTP request. - */ -public class BadHttpRequest extends Exception { - private Exception e; - - public BadHttpRequest() { e = null; } - - public BadHttpRequest(Exception _e) { e = _e; } - - public String toString() { - if (e == null) - return super.toString(); - else - return e.toString(); - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/web/Viewer.java b/src/com/wenshuo/agent/javassist/tools/web/Viewer.java deleted file mode 100644 index 52d5416..0000000 --- a/src/com/wenshuo/agent/javassist/tools/web/Viewer.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.web; - -import java.io.*; -import java.net.*; - -/** - * A sample applet viewer. - * - *

This is a sort of applet viewer that can run any program even if - * the main class is not a subclass of Applet. - * This viewwer first calls main() in the main class. - * - *

To run, you should type: - * - *

% java javassist.tools.web.Viewer host port Main arg1, ...
- * - *

This command calls Main.main() with arg1,... - * All classes including Main are fetched from - * a server http://host:port. - * Only the class file for Viewer must exist - * on a local file system at the client side; even other - * javassist.* classes are not needed at the client side. - * Viewer uses only Java core API classes. - * - *

Note: since a Viewer object is a class loader, - * a program loaded by this object can call a method in Viewer. - * For example, you can write something like this: - * - *

- * Viewer v = (Viewer)this.getClass().getClassLoader();
- * String port = v.getPort();
- * 
- * - */ -public class Viewer extends ClassLoader { - private String server; - private int port; - - /** - * Starts a program. - */ - public static void main(String[] args) throws Throwable { - if (args.length >= 3) { - Viewer cl = new Viewer(args[0], Integer.parseInt(args[1])); - String[] args2 = new String[args.length - 3]; - System.arraycopy(args, 3, args2, 0, args.length - 3); - cl.run(args[2], args2); - } - else - System.err.println( - "Usage: java javassist.tools.web.Viewer class [args ...]"); - } - - /** - * Constructs a viewer. - * - * @param host server name - * @param p port number - */ - public Viewer(String host, int p) { - server = host; - port = p; - } - - /** - * Returns the server name. - */ - public String getServer() { return server; } - - /** - * Returns the port number. - */ - public int getPort() { return port; } - - /** - * Invokes main() in the class specified by classname. - * - * @param classname executed class - * @param args the arguments passed to main(). - */ - public void run(String classname, String[] args) - throws Throwable - { - Class c = loadClass(classname); - try { - c.getDeclaredMethod("main", new Class[] { String[].class }) - .invoke(null, new Object[] { args }); - } - catch (java.lang.reflect.InvocationTargetException e) { - throw e.getTargetException(); - } - } - - /** - * Requests the class loader to load a class. - */ - protected synchronized Class loadClass(String name, boolean resolve) - throws ClassNotFoundException - { - Class c = findLoadedClass(name); - if (c == null) - c = findClass(name); - - if (c == null) - throw new ClassNotFoundException(name); - - if (resolve) - resolveClass(c); - - return c; - } - - /** - * Finds the specified class. The implementation in this class - * fetches the class from the http server. If the class is - * either java.*, javax.*, or - * Viewer, then it is loaded by the parent class - * loader. - * - *

This method can be overridden by a subclass of - * Viewer. - */ - protected Class findClass(String name) throws ClassNotFoundException { - Class c = null; - if (name.startsWith("java.") || name.startsWith("javax.") - || name.equals("javassist.tools.web.Viewer")) - c = findSystemClass(name); - - if (c == null) - try { - byte[] b = fetchClass(name); - if (b != null) - c = defineClass(name, b, 0, b.length); - } - catch (Exception e) { - } - - return c; - } - - /** - * Fetches the class file of the specified class from the http - * server. - */ - protected byte[] fetchClass(String classname) throws Exception - { - byte[] b; - URL url = new URL("http", server, port, - "/" + classname.replace('.', '/') + ".class"); - URLConnection con = url.openConnection(); - con.connect(); - int size = con.getContentLength(); - InputStream s = con.getInputStream(); - if (size <= 0) - b = readStream(s); - else { - b = new byte[size]; - int len = 0; - do { - int n = s.read(b, len, size - len); - if (n < 0) { - s.close(); - throw new IOException("the stream was closed: " - + classname); - } - len += n; - } while (len < size); - } - - s.close(); - return b; - } - - private byte[] readStream(InputStream fin) throws IOException { - byte[] buf = new byte[4096]; - int size = 0; - int len = 0; - do { - size += len; - if (buf.length - size <= 0) { - byte[] newbuf = new byte[buf.length * 2]; - System.arraycopy(buf, 0, newbuf, 0, size); - buf = newbuf; - } - - len = fin.read(buf, size, buf.length - size); - } while (len >= 0); - - byte[] result = new byte[size]; - System.arraycopy(buf, 0, result, 0, size); - return result; - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/web/Webserver.java b/src/com/wenshuo/agent/javassist/tools/web/Webserver.java deleted file mode 100644 index 5511ef2..0000000 --- a/src/com/wenshuo/agent/javassist/tools/web/Webserver.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.tools.web; - -import java.net.*; -import java.io.*; -import java.util.Date; -import com.wenshuo.agent.javassist.*; - -/** - * A web server for running sample programs. - * - *

This enables a Java program to instrument class files loaded by - * web browsers for applets. Since the (standard) security manager - * does not allow an applet to create and use a class loader, - * instrumenting class files must be done by this web server. - * - *

Note: although this class is included in the Javassist API, - * it is provided as a sample implementation of the web server using - * Javassist. Especially, there might be security flaws in this server. - * Please use this with YOUR OWN RISK. - */ -public class Webserver { - private ServerSocket socket; - private ClassPool classPool; - protected Translator translator; - - private final static byte[] endofline = { 0x0d, 0x0a }; - - private final static int typeHtml = 1; - private final static int typeClass = 2; - private final static int typeGif = 3; - private final static int typeJpeg = 4; - private final static int typeText = 5; - - /** - * If this field is not null, the class files taken from - * ClassPool are written out under the directory - * specified by this field. The directory name must not end - * with a directory separator. - */ - public String debugDir = null; - - /** - * The top directory of html (and .gif, .class, ...) files. - * It must end with the directory separator such as "/". - * (For portability, "/" should be used as the directory separator. - * Javassist automatically translates "/" into a platform-dependent - * character.) - * If this field is null, the top directory is the current one where - * the JVM is running. - * - *

If the given URL indicates a class file and the class file - * is not found under the directory specified by this variable, - * then Class.getResourceAsStream() is called - * for searching the Java class paths. - */ - public String htmlfileBase = null; - - /** - * Starts a web server. - * The port number is specified by the first argument. - */ - public static void main(String[] args) throws IOException { - if (args.length == 1) { - Webserver web = new Webserver(args[0]); - web.run(); - } - else - System.err.println( - "Usage: java javassist.tools.web.Webserver "); - } - - /** - * Constructs a web server. - * - * @param port port number - */ - public Webserver(String port) throws IOException { - this(Integer.parseInt(port)); - } - - /** - * Constructs a web server. - * - * @param port port number - */ - public Webserver(int port) throws IOException { - socket = new ServerSocket(port); - classPool = null; - translator = null; - } - - /** - * Requests the web server to use the specified - * ClassPool object for obtaining a class file. - */ - public void setClassPool(ClassPool loader) { - classPool = loader; - } - - /** - * Adds a translator, which is called whenever a client requests - * a class file. - * - * @param cp the ClassPool object for obtaining - * a class file. - * @param t a translator. - */ - public void addTranslator(ClassPool cp, Translator t) - throws NotFoundException, CannotCompileException - { - classPool = cp; - translator = t; - t.start(classPool); - } - - /** - * Closes the socket. - */ - public void end() throws IOException { - socket.close(); - } - - /** - * Prints a log message. - */ - public void logging(String msg) { - System.out.println(msg); - } - - /** - * Prints a log message. - */ - public void logging(String msg1, String msg2) { - System.out.print(msg1); - System.out.print(" "); - System.out.println(msg2); - } - - /** - * Prints a log message. - */ - public void logging(String msg1, String msg2, String msg3) { - System.out.print(msg1); - System.out.print(" "); - System.out.print(msg2); - System.out.print(" "); - System.out.println(msg3); - } - - /** - * Prints a log message with indentation. - */ - public void logging2(String msg) { - System.out.print(" "); - System.out.println(msg); - } - - /** - * Begins the HTTP service. - */ - public void run() { - System.err.println("ready to service..."); - for (;;) - try { - ServiceThread th = new ServiceThread(this, socket.accept()); - th.start(); - } - catch (IOException e) { - logging(e.toString()); - } - } - - final void process(Socket clnt) throws IOException { - InputStream in = new BufferedInputStream(clnt.getInputStream()); - String cmd = readLine(in); - logging(clnt.getInetAddress().getHostName(), - new Date().toString(), cmd); - while (skipLine(in) > 0){ - } - - OutputStream out = new BufferedOutputStream(clnt.getOutputStream()); - try { - doReply(in, out, cmd); - } - catch (BadHttpRequest e) { - replyError(out, e); - } - - out.flush(); - in.close(); - out.close(); - clnt.close(); - } - - private String readLine(InputStream in) throws IOException { - StringBuffer buf = new StringBuffer(); - int c; - while ((c = in.read()) >= 0 && c != 0x0d) - buf.append((char)c); - - in.read(); /* skip 0x0a (LF) */ - return buf.toString(); - } - - private int skipLine(InputStream in) throws IOException { - int c; - int len = 0; - while ((c = in.read()) >= 0 && c != 0x0d) - ++len; - - in.read(); /* skip 0x0a (LF) */ - return len; - } - - /** - * Proceses a HTTP request from a client. - * - * @param out the output stream to a client - * @param cmd the command received from a client - */ - public void doReply(InputStream in, OutputStream out, String cmd) - throws IOException, BadHttpRequest - { - int len; - int fileType; - String filename, urlName; - - if (cmd.startsWith("GET /")) - filename = urlName = cmd.substring(5, cmd.indexOf(' ', 5)); - else - throw new BadHttpRequest(); - - if (filename.endsWith(".class")) - fileType = typeClass; - else if (filename.endsWith(".html") || filename.endsWith(".htm")) - fileType = typeHtml; - else if (filename.endsWith(".gif")) - fileType = typeGif; - else if (filename.endsWith(".jpg")) - fileType = typeJpeg; - else - fileType = typeText; // or textUnknown - - len = filename.length(); - if (fileType == typeClass - && letUsersSendClassfile(out, filename, len)) - return; - - checkFilename(filename, len); - if (htmlfileBase != null) - filename = htmlfileBase + filename; - - if (File.separatorChar != '/') - filename = filename.replace('/', File.separatorChar); - - File file = new File(filename); - if (file.canRead()) { - sendHeader(out, file.length(), fileType); - FileInputStream fin = new FileInputStream(file); - byte[] filebuffer = new byte[4096]; - for (;;) { - len = fin.read(filebuffer); - if (len <= 0) - break; - else - out.write(filebuffer, 0, len); - } - - fin.close(); - return; - } - - // If the file is not found under the html-file directory, - // then Class.getResourceAsStream() is tried. - - if (fileType == typeClass) { - InputStream fin - = getClass().getResourceAsStream("/" + urlName); - if (fin != null) { - ByteArrayOutputStream barray = new ByteArrayOutputStream(); - byte[] filebuffer = new byte[4096]; - for (;;) { - len = fin.read(filebuffer); - if (len <= 0) - break; - else - barray.write(filebuffer, 0, len); - } - - byte[] classfile = barray.toByteArray(); - sendHeader(out, classfile.length, typeClass); - out.write(classfile); - fin.close(); - return; - } - } - - throw new BadHttpRequest(); - } - - private void checkFilename(String filename, int len) - throws BadHttpRequest - { - for (int i = 0; i < len; ++i) { - char c = filename.charAt(i); - if (!Character.isJavaIdentifierPart(c) && c != '.' && c != '/') - throw new BadHttpRequest(); - } - - if (filename.indexOf("..") >= 0) - throw new BadHttpRequest(); - } - - private boolean letUsersSendClassfile(OutputStream out, - String filename, int length) - throws IOException, BadHttpRequest - { - if (classPool == null) - return false; - - byte[] classfile; - String classname - = filename.substring(0, length - 6).replace('/', '.'); - try { - if (translator != null) - translator.onLoad(classPool, classname); - - CtClass c = classPool.get(classname); - classfile = c.toBytecode(); - if (debugDir != null) - c.writeFile(debugDir); - } - catch (Exception e) { - throw new BadHttpRequest(e); - } - - sendHeader(out, classfile.length, typeClass); - out.write(classfile); - return true; - } - - private void sendHeader(OutputStream out, long dataLength, int filetype) - throws IOException - { - out.write("HTTP/1.0 200 OK".getBytes()); - out.write(endofline); - out.write("Content-Length: ".getBytes()); - out.write(Long.toString(dataLength).getBytes()); - out.write(endofline); - if (filetype == typeClass) - out.write("Content-Type: application/octet-stream".getBytes()); - else if (filetype == typeHtml) - out.write("Content-Type: text/html".getBytes()); - else if (filetype == typeGif) - out.write("Content-Type: image/gif".getBytes()); - else if (filetype == typeJpeg) - out.write("Content-Type: image/jpg".getBytes()); - else if (filetype == typeText) - out.write("Content-Type: text/plain".getBytes()); - - out.write(endofline); - out.write(endofline); - } - - private void replyError(OutputStream out, BadHttpRequest e) - throws IOException - { - logging2("bad request: " + e.toString()); - out.write("HTTP/1.0 400 Bad Request".getBytes()); - out.write(endofline); - out.write(endofline); - out.write("

Bad Request

".getBytes()); - } -} - -class ServiceThread extends Thread { - Webserver web; - Socket sock; - - public ServiceThread(Webserver w, Socket s) { - web = w; - sock = s; - } - - public void run() { - try { - web.process(sock); - } - catch (IOException e) { - } - } -} diff --git a/src/com/wenshuo/agent/javassist/tools/web/package.html b/src/com/wenshuo/agent/javassist/tools/web/package.html deleted file mode 100644 index 0c7fb45..0000000 --- a/src/com/wenshuo/agent/javassist/tools/web/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Simple web server for running sample code. - -

This package provides a simple web server for sample packages. - - diff --git a/src/com/wenshuo/agent/javassist/util/HotSwapper.java b/src/com/wenshuo/agent/javassist/util/HotSwapper.java deleted file mode 100644 index 45836bf..0000000 --- a/src/com/wenshuo/agent/javassist/util/HotSwapper.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util; - -import com.sun.jdi.*; -import com.sun.jdi.connect.*; -import com.sun.jdi.event.*; -import com.sun.jdi.request.*; -import java.io.*; -import java.util.*; - -class Trigger { - void doSwap() {} -} - -/** - * A utility class for dynamically reloading a class by - * the Java Platform Debugger Architecture (JPDA), or HotSwap. - * It works only with JDK 1.4 and later. - * - *

Note: The new definition of the reloaded class must declare - * the same set of methods and fields as the original definition. The - * schema change between the original and new definitions is not allowed - * by the JPDA. - * - *

To use this class, the JVM must be launched with the following - * command line options: - * - *

For Java 1.4,
- *

java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
- *

For Java 5,
- *

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
- * - *

Note that 8000 is the port number used by HotSwapper. - * Any port number can be specified. Since HotSwapper does not - * launch another JVM for running a target application, this port number - * is used only for inter-thread communication. - * - *

Furthermore, JAVA_HOME/lib/tools.jar must be included - * in the class path. - * - *

Using HotSwapper is easy. See the following example: - * - *

- * CtClass clazz = ...
- * byte[] classFile = clazz.toBytecode();
- * HotSwapper hs = new HostSwapper(8000);  // 8000 is a port number.
- * hs.reload("Test", classFile);
- * 
- * - *

reload() - * first unload the Test class and load a new version of - * the Test class. - * classFile is a byte array containing the new contents of - * the class file for the Test class. The developers can - * repatedly call reload() on the same HotSwapper - * object so that they can reload a number of classes. - * - * @since 3.1 - */ -public class HotSwapper { - private VirtualMachine jvm; - private MethodEntryRequest request; - private Map newClassFiles; - - private Trigger trigger; - - private static final String HOST_NAME = "localhost"; - private static final String TRIGGER_NAME = Trigger.class.getName(); - - /** - * Connects to the JVM. - * - * @param port the port number used for the connection to the JVM. - */ - public HotSwapper(int port) - throws IOException, IllegalConnectorArgumentsException - { - this(Integer.toString(port)); - } - - /** - * Connects to the JVM. - * - * @param port the port number used for the connection to the JVM. - */ - public HotSwapper(String port) - throws IOException, IllegalConnectorArgumentsException - { - jvm = null; - request = null; - newClassFiles = null; - trigger = new Trigger(); - AttachingConnector connector - = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach"); - - Map arguments = connector.defaultArguments(); - ((Connector.Argument)arguments.get("hostname")).setValue(HOST_NAME); - ((Connector.Argument)arguments.get("port")).setValue(port); - jvm = connector.attach(arguments); - EventRequestManager manager = jvm.eventRequestManager(); - request = methodEntryRequests(manager, TRIGGER_NAME); - } - - private Connector findConnector(String connector) throws IOException { - List connectors = Bootstrap.virtualMachineManager().allConnectors(); - Iterator iter = connectors.iterator(); - while (iter.hasNext()) { - Connector con = (Connector)iter.next(); - if (con.name().equals(connector)) { - return con; - } - } - - throw new IOException("Not found: " + connector); - } - - private static MethodEntryRequest methodEntryRequests( - EventRequestManager manager, - String classpattern) { - MethodEntryRequest mereq = manager.createMethodEntryRequest(); - mereq.addClassFilter(classpattern); - mereq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); - return mereq; - } - - /* Stops triggering a hotswapper when reload() is called. - */ - private void deleteEventRequest(EventRequestManager manager, - MethodEntryRequest request) { - manager.deleteEventRequest(request); - } - - /** - * Reloads a class. - * - * @param className the fully-qualified class name. - * @param classFile the contents of the class file. - */ - public void reload(String className, byte[] classFile) { - ReferenceType classtype = toRefType(className); - Map map = new HashMap(); - map.put(classtype, classFile); - reload2(map, className); - } - - /** - * Reloads a class. - * - * @param classFiles a map between fully-qualified class names - * and class files. The type of the class names - * is String and the type of the - * class files is byte[]. - */ - public void reload(Map classFiles) { - Set set = classFiles.entrySet(); - Iterator it = set.iterator(); - Map map = new HashMap(); - String className = null; - while (it.hasNext()) { - Map.Entry e = (Map.Entry)it.next(); - className = (String)e.getKey(); - map.put(toRefType(className), e.getValue()); - } - - if (className != null) - reload2(map, className + " etc."); - } - - private ReferenceType toRefType(String className) { - List list = jvm.classesByName(className); - if (list == null || list.isEmpty()) - throw new RuntimeException("no such class: " + className); - else - return (ReferenceType)list.get(0); - } - - private void reload2(Map map, String msg) { - synchronized (trigger) { - startDaemon(); - newClassFiles = map; - request.enable(); - trigger.doSwap(); - request.disable(); - Map ncf = newClassFiles; - if (ncf != null) { - newClassFiles = null; - throw new RuntimeException("failed to reload: " + msg); - } - } - } - - private void startDaemon() { - new Thread() { - private void errorMsg(Throwable e) { - System.err.print("Exception in thread \"HotSwap\" "); - e.printStackTrace(System.err); - } - - public void run() { - EventSet events = null; - try { - events = waitEvent(); - EventIterator iter = events.eventIterator(); - while (iter.hasNext()) { - Event event = iter.nextEvent(); - if (event instanceof MethodEntryEvent) { - hotswap(); - break; - } - } - } - catch (Throwable e) { - errorMsg(e); - } - try { - if (events != null) - events.resume(); - } - catch (Throwable e) { - errorMsg(e); - } - } - }.start(); - } - - EventSet waitEvent() throws InterruptedException { - EventQueue queue = jvm.eventQueue(); - return queue.remove(); - } - - void hotswap() { - Map map = newClassFiles; - jvm.redefineClasses(map); - newClassFiles = null; - } -} diff --git a/src/com/wenshuo/agent/javassist/util/package.html b/src/com/wenshuo/agent/javassist/util/package.html deleted file mode 100644 index 349d996..0000000 --- a/src/com/wenshuo/agent/javassist/util/package.html +++ /dev/null @@ -1,5 +0,0 @@ - - -Utility classes. - - diff --git a/src/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java b/src/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java deleted file mode 100644 index 02f961e..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -import java.lang.reflect.Method; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.security.ProtectionDomain; - -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.bytecode.ClassFile; - -/** - * A helper class for implementing ProxyFactory. - * The users of ProxyFactory do not have to see this class. - * - * @see ProxyFactory - */ -public class FactoryHelper { - private static java.lang.reflect.Method defineClass1, defineClass2; - - static { - try { - Class cl = Class.forName("java.lang.ClassLoader"); - defineClass1 = SecurityActions.getDeclaredMethod( - cl, - "defineClass", - new Class[] { String.class, byte[].class, - int.class, int.class }); - - defineClass2 = SecurityActions.getDeclaredMethod( - cl, - "defineClass", - new Class[] { String.class, byte[].class, - int.class, int.class, ProtectionDomain.class }); - } - catch (Exception e) { - throw new RuntimeException("cannot initialize"); - } - } - - /** - * Returns an index for accessing arrays in this class. - * - * @throws RuntimeException if a given type is not a primitive type. - */ - public static final int typeIndex(Class type) { - Class[] list = primitiveTypes; - int n = list.length; - for (int i = 0; i < n; i++) - if (list[i] == type) - return i; - - throw new RuntimeException("bad type:" + type.getName()); - } - - /** - * Class objects representing primitive types. - */ - public static final Class[] primitiveTypes = { - Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, - Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE - }; - - /** - * The fully-qualified names of wrapper classes for primitive types. - */ - public static final String[] wrapperTypes = { - "java.lang.Boolean", "java.lang.Byte", "java.lang.Character", - "java.lang.Short", "java.lang.Integer", "java.lang.Long", - "java.lang.Float", "java.lang.Double", "java.lang.Void" - }; - - /** - * The descriptors of the constructors of wrapper classes. - */ - public static final String[] wrapperDesc = { - "(Z)V", "(B)V", "(C)V", "(S)V", "(I)V", "(J)V", - "(F)V", "(D)V" - }; - - /** - * The names of methods for obtaining a primitive value - * from a wrapper object. For example, intValue() - * is such a method for obtaining an integer value from a - * java.lang.Integer object. - */ - public static final String[] unwarpMethods = { - "booleanValue", "byteValue", "charValue", "shortValue", - "intValue", "longValue", "floatValue", "doubleValue" - }; - - /** - * The descriptors of the unwrapping methods contained - * in unwrapMethods. - */ - public static final String[] unwrapDesc = { - "()Z", "()B", "()C", "()S", "()I", "()J", "()F", "()D" - }; - - /** - * The data size of primitive types. long - * and double are 2; the others are 1. - */ - public static final int[] dataSize = { - 1, 1, 1, 1, 1, 2, 1, 2 - }; - - /** - * Loads a class file by a given class loader. - * This method uses a default protection domain for the class - * but it may not work with a security manager or a sigend jar file. - * - * @see #toClass(ClassFile,ClassLoader,ProtectionDomain) - */ - public static Class toClass(ClassFile cf, ClassLoader loader) - throws CannotCompileException - { - return toClass(cf, loader, null); - } - - /** - * Loads a class file by a given class loader. - * - * @param domain if it is null, a default domain is used. - * @since 3.3 - */ - public static Class toClass(ClassFile cf, ClassLoader loader, ProtectionDomain domain) - throws CannotCompileException - { - try { - byte[] b = toBytecode(cf); - Method method; - Object[] args; - if (domain == null) { - method = defineClass1; - args = new Object[] { cf.getName(), b, new Integer(0), - new Integer(b.length) }; - } - else { - method = defineClass2; - args = new Object[] { cf.getName(), b, new Integer(0), - new Integer(b.length), domain }; - } - - return toClass2(method, loader, args); - } - catch (RuntimeException e) { - throw e; - } - catch (java.lang.reflect.InvocationTargetException e) { - throw new CannotCompileException(e.getTargetException()); - } - catch (Exception e) { - throw new CannotCompileException(e); - } - } - - private static synchronized Class toClass2(Method method, - ClassLoader loader, Object[] args) - throws Exception - { - SecurityActions.setAccessible(method, true); - Class clazz = (Class)method.invoke(loader, args); - SecurityActions.setAccessible(method, false); - return clazz; - } - - private static byte[] toBytecode(ClassFile cf) throws IOException { - ByteArrayOutputStream barray = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(barray); - try { - cf.write(out); - } - finally { - out.close(); - } - - return barray.toByteArray(); - } - - /** - * Writes a class file. - */ - public static void writeFile(ClassFile cf, String directoryName) - throws CannotCompileException { - try { - writeFile0(cf, directoryName); - } - catch (IOException e) { - throw new CannotCompileException(e); - } - } - - private static void writeFile0(ClassFile cf, String directoryName) - throws CannotCompileException, IOException { - String classname = cf.getName(); - String filename = directoryName + File.separatorChar - + classname.replace('.', File.separatorChar) + ".class"; - int pos = filename.lastIndexOf(File.separatorChar); - if (pos > 0) { - String dir = filename.substring(0, pos); - if (!dir.equals(".")) - new File(dir).mkdirs(); - } - - DataOutputStream out = new DataOutputStream(new BufferedOutputStream( - new FileOutputStream(filename))); - try { - cf.write(out); - } - catch (IOException e) { - throw e; - } - finally { - out.close(); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java b/src/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java deleted file mode 100644 index 4ebe168..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -import java.lang.reflect.Method; - -/** - * Selector of the methods implemented by a handler. - * - * @see ProxyFactory#setFilter(MethodFilter) - */ -public interface MethodFilter { - /** - * Returns true if the given method is implemented by a handler. - */ - boolean isHandled(Method m); -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java b/src/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java deleted file mode 100644 index 546dc76..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -import java.lang.reflect.Method; - -/** - * The interface implemented by the invocation handler of a proxy - * instance. - * - * @see Proxy#setHandler(MethodHandler) - */ -public interface MethodHandler { - /** - * Is called when a method is invoked on a proxy instance associated - * with this handler. This method must process that method invocation. - * - * @param self the proxy instance. - * @param thisMethod the overridden method declared in the super - * class or interface. - * @param proceed the forwarder method for invoking the overridden - * method. It is null if the overridden method is - * abstract or declared in the interface. - * @param args an array of objects containing the values of - * the arguments passed in the method invocation - * on the proxy instance. If a parameter type is - * a primitive type, the type of the array element - * is a wrapper class. - * @return the resulting value of the method invocation. - * - * @throws Throwable if the method invocation fails. - */ - Object invoke(Object self, Method thisMethod, Method proceed, - Object[] args) throws Throwable; -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/Proxy.java b/src/com/wenshuo/agent/javassist/util/proxy/Proxy.java deleted file mode 100644 index 3907742..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/Proxy.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -/** - * The interface implemented by proxy classes. - * This interface only provides a setter method. - * To obtain a handler, call {@link ProxyFactory#getHandler(Proxy)}. - * - * @see ProxyFactory - * @since 3.16 - */ -public interface Proxy { - /** - * Sets a handler. It can be used for changing handlers - * during runtime. - */ - void setHandler(MethodHandler mi); -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java b/src/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java deleted file mode 100644 index 270fc56..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java +++ /dev/null @@ -1,1446 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Constructor; -import java.lang.reflect.Member; -import java.lang.reflect.Modifier; -import java.security.ProtectionDomain; -import java.util.*; -import java.lang.ref.WeakReference; - -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.NotFoundException; -import com.wenshuo.agent.javassist.bytecode.*; - -/* - * This class is implemented only with the lower-level API of Javassist. - * This design decision is for maximizing performance. - */ - -/** - * Factory of dynamic proxy classes. - * - *

This factory generates a class that extends the given super class and implements - * the given interfaces. The calls of the methods inherited from the super class are - * forwarded and then invoke() is called on the method handler - * associated with instances of the generated class. The calls of the methods from - * the interfaces are also forwarded to the method handler. - * - *

For example, if the following code is executed, - * - *

- * ProxyFactory f = new ProxyFactory();
- * f.setSuperclass(Foo.class);
- * f.setFilter(new MethodFilter() {
- *     public boolean isHandled(Method m) {
- *         // ignore finalize()
- *         return !m.getName().equals("finalize");
- *     }
- * });
- * Class c = f.createClass();
- * MethodHandler mi = new MethodHandler() {
- *     public Object invoke(Object self, Method m, Method proceed,
- *                          Object[] args) throws Throwable {
- *         System.out.println("Name: " + m.getName());
- *         return proceed.invoke(self, args);  // execute the original method.
- *     }
- * };
- * Foo foo = (Foo)c.newInstance();
- * ((Proxy)foo).setHandler(mi);
- * 
- * - *

Here, Method is java.lang.reflect.Method.

- * - *

Then, the following method call will be forwarded to MethodHandler - * mi and prints a message before executing the originally called method - * bar() in Foo. - * - *

- * foo.bar();
- * 
- * - *

The last three lines of the code shown above can be replaced with a call to - * the helper method create, which generates a proxy class, instantiates - * it, and sets the method handler of the instance: - * - *

- *     :
- * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi);
- * 
- * - *

To change the method handler during runtime, - * execute the following code: - * - *

- * MethodHandler mi = ... ;    // alternative handler
- * ((Proxy)foo).setHandler(mi);
- * 
- * - *

If setHandler is never called for a proxy instance then it will - * employ the default handler which proceeds by invoking the original method. - * The behaviour of the default handler is identical to the following - * handler: - * - *

- * class EmptyHandler implements MethodHandler {
- *     public Object invoke(Object self, Method m,
- *                          Method proceed, Object[] args) throws Exception {
- *         return proceed.invoke(self, args);
- *     }
- * }
- * 
- * - *

A proxy factory caches and reuses proxy classes by default. It is possible to reset - * this default globally by setting static field {@link ProxyFactory#useCache} to false. - * Caching may also be configured for a specific factory by calling instance method - * {@link ProxyFactory#setUseCache(boolean)}. It is strongly recommended that new clients - * of class ProxyFactory enable caching. Failure to do so may lead to exhaustion of - * the heap memory area used to store classes. - * - *

Caching is automatically disabled for any given proxy factory if deprecated instance - * method {@link ProxyFactory#setHandler(MethodHandler)} is called. This method was - * used to specify a default handler which newly created proxy classes should install - * when they create their instances. It is only retained to provide backward compatibility - * with previous releases of javassist. Unfortunately,this legacy behaviour makes caching - * and reuse of proxy classes impossible. The current programming model expects javassist - * clients to set the handler of a proxy instance explicitly by calling method - * {@link Proxy#setHandler(MethodHandler)} as shown in the sample code above. New - * clients are strongly recommended to use this model rather than calling - * {@link ProxyFactory#setHandler(MethodHandler)}. - * - *

A proxy object generated by ProxyFactory is serializable - * if its super class or any of its interfaces implement java.io.Serializable. - * However, a serialized proxy object may not be compatible with future releases. - * The serialization support should be used for short-term storage or RMI. - * - *

For compatibility with older releases serialization of proxy objects is implemented by - * adding a writeReplace method to the proxy class. This allows a proxy to be serialized - * to a conventional {@link java.io.ObjectOutputStream} and deserialized from a corresponding - * {@link java.io.ObjectInputStream}. However this method suffers from several problems, the most - * notable one being that it fails to serialize state inherited from the proxy's superclass. - *

- * An alternative method of serializing proxy objects is available which fixes these problems. It - * requires inhibiting generation of the writeReplace method and instead using instances of - * {@link javassist.util.proxy.ProxyObjectOutputStream} and {@link javassist.util.proxy.ProxyObjectInputStream} - * (which are subclasses of {@link java.io.ObjectOutputStream} and {@link java.io.ObjectInputStream}) - * to serialize and deserialize, respectively, the proxy. These streams recognise javassist proxies and ensure - * that they are serialized and deserialized without the need for the proxy class to implement special methods - * such as writeReplace. Generation of the writeReplace method can be disabled globally by setting static field - * {@link ProxyFactory#useWriteReplace} to false. Alternatively, it may be - * configured per factory by calling instance method {@link ProxyFactory#setUseWriteReplace(boolean)}. - * - * @see MethodHandler - * @since 3.1 - * @author Muga Nishizawa - * @author Shigeru Chiba - * @author Andrew Dinn - */ -public class ProxyFactory { - private Class superClass; - private Class[] interfaces; - private MethodFilter methodFilter; - private MethodHandler handler; // retained for legacy usage - private List signatureMethods; - private boolean hasGetHandler; - private byte[] signature; - private String classname; - private String basename; - private String superName; - private Class thisClass; - /** - * per factory setting initialised from current setting for useCache but able to be reset before each create call - */ - private boolean factoryUseCache; - /** - * per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call - */ - private boolean factoryWriteReplace; - - - /** - * If the value of this variable is not null, the class file of - * the generated proxy class is written under the directory specified - * by this variable. For example, if the value is - * ".", then the class file is written under the current - * directory. This method is for debugging. - * - *

The default value is null. - */ - public String writeDirectory; - - private static final Class OBJECT_TYPE = Object.class; - - private static final String HOLDER = "_methods_"; - private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;"; - private static final String FILTER_SIGNATURE_FIELD = "_filter_signature"; - private static final String FILTER_SIGNATURE_TYPE = "[B"; - private static final String HANDLER = "handler"; - private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport"; - private static final String DEFAULT_INTERCEPTOR = "default_interceptor"; - private static final String HANDLER_TYPE - = 'L' + MethodHandler.class.getName().replace('.', '/') + ';'; - private static final String HANDLER_SETTER = "setHandler"; - private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V"; - - private static final String HANDLER_GETTER = "getHandler"; - private static final String HANDLER_GETTER_TYPE = "()" + HANDLER_TYPE; - - private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID"; - private static final String SERIAL_VERSION_UID_TYPE = "J"; - private static final long SERIAL_VERSION_UID_VALUE = -1L; - - /** - * If true, a generated proxy class is cached and it will be reused - * when generating the proxy class with the same properties is requested. - * The default value is true. - * - * Note that this value merely specifies the initial setting employed by any newly created - * proxy factory. The factory setting may be overwritten by calling factory instance method - * {@link #setUseCache(boolean)} - * - * @since 3.4 - */ - public static volatile boolean useCache = true; - - /** - * If true, a generated proxy class will implement method writeReplace enabling - * serialization of its proxies to a conventional ObjectOutputStream. this (default) - * setting retains the old javassist behaviour which has the advantage that it - * retains compatibility with older releases and requires no extra work on the part - * of the client performing the serialization. However, it has the disadvantage that - * state inherited from the superclasses of the proxy is lost during serialization. - * if false then serialization/deserialization of the proxy instances will preserve - * all fields. However, serialization must be performed via a {@link ProxyObjectOutputStream} - * and deserialization must be via {@link ProxyObjectInputStream}. Any attempt to serialize - * proxies whose class was created with useWriteReplace set to false via a normal - * {@link java.io.ObjectOutputStream} will fail. - * - * Note that this value merely specifies the initial setting employed by any newly created - * proxy factory. The factory setting may be overwritten by calling factory instance method - * {@link #setUseWriteReplace(boolean)} - * - * @since 3.4 - */ - public static volatile boolean useWriteReplace = true; - - /* - * methods allowing individual factory settings for factoryUseCache and factoryWriteReplace to be reset - */ - - /** - * test whether this factory uses the proxy cache - * @return true if this factory uses the proxy cache otherwise false - */ - public boolean isUseCache() - { - return factoryUseCache; - } - - /** - * configure whether this factory should use the proxy cache - * @param useCache true if this factory should use the proxy cache and false if it should not use the cache - * @throws RuntimeException if a default interceptor has been set for the factory - */ - public void setUseCache(boolean useCache) - { - // we cannot allow caching to be used if the factory is configured to install a default interceptor - // field into generated classes - if (handler != null && useCache) { - throw new RuntimeException("caching cannot be enabled if the factory default interceptor has been set"); - } - factoryUseCache = useCache; - } - - /** - * test whether this factory installs a writeReplace method in created classes - * @return true if this factory installs a writeReplace method in created classes otherwise false - */ - public boolean isUseWriteReplace() - { - return factoryWriteReplace; - } - - /** - * configure whether this factory should add a writeReplace method to created classes - * @param useWriteReplace true if this factory should add a writeReplace method to created classes and false if it - * should not add a writeReplace method - */ - public void setUseWriteReplace(boolean useWriteReplace) - { - factoryWriteReplace = useWriteReplace; - } - - private static WeakHashMap proxyCache = new WeakHashMap(); - - /** - * determine if a class is a javassist proxy class - * @param cl - * @return true if the class is a javassist proxy class otherwise false - */ - public static boolean isProxyClass(Class cl) - { - // all proxies implement Proxy or ProxyObject. nothing else should. - return (Proxy.class.isAssignableFrom(cl)); - } - - /** - * used to store details of a specific proxy class in the second tier of the proxy cache. this entry - * will be located in a hashmap keyed by the unique identifying name of the proxy class. the hashmap is - * located in a weak hashmap keyed by the classloader common to all proxy classes in the second tier map. - */ - static class ProxyDetails { - /** - * the unique signature of any method filter whose behaviour will be met by this class. each bit in - * the byte array is set if the filter redirects the corresponding super or interface method and clear - * if it does not redirect it. - */ - byte[] signature; - /** - * a hexadecimal string representation of the signature bit sequence. this string also forms part - * of the proxy class name. - */ - WeakReference proxyClass; - /** - * a flag which is true this class employs writeReplace to perform serialization of its instances - * and false if serialization must employ of a ProxyObjectOutputStream and ProxyObjectInputStream - */ - boolean isUseWriteReplace; - - ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace) - { - this.signature = signature; - this.proxyClass = new WeakReference(proxyClass); - this.isUseWriteReplace = isUseWriteReplace; - } - } - - /** - * Constructs a factory of proxy class. - */ - public ProxyFactory() { - superClass = null; - interfaces = null; - methodFilter = null; - handler = null; - signature = null; - signatureMethods = null; - hasGetHandler = false; - thisClass = null; - writeDirectory = null; - factoryUseCache = useCache; - factoryWriteReplace = useWriteReplace; - } - - /** - * Sets the super class of a proxy class. - */ - public void setSuperclass(Class clazz) { - superClass = clazz; - // force recompute of signature - signature = null; - } - - /** - * Obtains the super class set by setSuperclass(). - * - * @since 3.4 - */ - public Class getSuperclass() { return superClass; } - - /** - * Sets the interfaces of a proxy class. - */ - public void setInterfaces(Class[] ifs) { - interfaces = ifs; - // force recompute of signature - signature = null; - } - - /** - * Obtains the interfaces set by setInterfaces. - * - * @since 3.4 - */ - public Class[] getInterfaces() { return interfaces; } - - /** - * Sets a filter that selects the methods that will be controlled by a handler. - */ - public void setFilter(MethodFilter mf) { - methodFilter = mf; - // force recompute of signature - signature = null; - } - - /** - * Generates a proxy class using the current filter. - */ - public Class createClass() { - if (signature == null) { - computeSignature(methodFilter); - } - return createClass1(); - } - - /** - * Generates a proxy class using the supplied filter. - */ - public Class createClass(MethodFilter filter) { - computeSignature(filter); - return createClass1(); - } - - /** - * Generates a proxy class with a specific signature. - * access is package local so ProxyObjectInputStream can use this - * @param signature - * @return - */ - Class createClass(byte[] signature) - { - installSignature(signature); - return createClass1(); - } - - private Class createClass1() { - if (thisClass == null) { - ClassLoader cl = getClassLoader(); - synchronized (proxyCache) { - if (factoryUseCache) - createClass2(cl); - else - createClass3(cl); - } - } - - // don't retain any unwanted references - Class result = thisClass; - thisClass = null; - - return result; - } - - private static char[] hexDigits = - { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - - public String getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace) - { - StringBuffer sbuf = new StringBuffer(); - if (superClass != null){ - sbuf.append(superClass.getName()); - } - sbuf.append(":"); - for (int i = 0; i < interfaces.length; i++) { - sbuf.append(interfaces[i].getName()); - sbuf.append(":"); - } - for (int i = 0; i < signature.length; i++) { - byte b = signature[i]; - int lo = b & 0xf; - int hi = (b >> 4) & 0xf; - sbuf.append(hexDigits[lo]); - sbuf.append(hexDigits[hi]); - } - if (useWriteReplace) { - sbuf.append(":w"); - } - - return sbuf.toString(); - } - - private void createClass2(ClassLoader cl) { - String key = getKey(superClass, interfaces, signature, factoryWriteReplace); - /* - * Excessive concurrency causes a large memory footprint and slows the - * execution speed down (with JDK 1.5). Thus, we use a jumbo lock for - * reducing concrrency. - */ - // synchronized (proxyCache) { - HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl); - ProxyDetails details; - if (cacheForTheLoader == null) { - cacheForTheLoader = new HashMap(); - proxyCache.put(cl, cacheForTheLoader); - } - details = (ProxyDetails)cacheForTheLoader.get(key); - if (details != null) { - WeakReference reference = details.proxyClass; - thisClass = (Class)reference.get(); - if (thisClass != null) { - return; - } - } - createClass3(cl); - details = new ProxyDetails(signature, thisClass, factoryWriteReplace); - cacheForTheLoader.put(key, details); - // } - } - - private void createClass3(ClassLoader cl) { - // we need a new class so we need a new class name - allocateClassName(); - - try { - ClassFile cf = make(); - if (writeDirectory != null) - FactoryHelper.writeFile(cf, writeDirectory); - - thisClass = FactoryHelper.toClass(cf, cl, getDomain()); - setField(FILTER_SIGNATURE_FIELD, signature); - // legacy behaviour : we only set the default interceptor static field if we are not using the cache - if (!factoryUseCache) { - setField(DEFAULT_INTERCEPTOR, handler); - } - } - catch (CannotCompileException e) { - throw new RuntimeException(e.getMessage(), e); - } - - } - - private void setField(String fieldName, Object value) { - if (thisClass != null && value != null) - try { - Field f = thisClass.getField(fieldName); - SecurityActions.setAccessible(f, true); - f.set(null, value); - SecurityActions.setAccessible(f, false); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - static byte[] getFilterSignature(Class clazz) { - return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD); - } - - private static Object getField(Class clazz, String fieldName) { - try { - Field f = clazz.getField(fieldName); - f.setAccessible(true); - Object value = f.get(null); - f.setAccessible(false); - return value; - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Obtains the method handler of the given proxy object. - * - * @param p a proxy object. - * @return the method handler. - * @since 3.16 - */ - public static MethodHandler getHandler(Proxy p) { - try { - Field f = p.getClass().getDeclaredField(HANDLER); - f.setAccessible(true); - Object value = f.get(p); - f.setAccessible(false); - return (MethodHandler)value; - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * A provider of class loaders. - * - * @see #classLoaderProvider - * @since 3.4 - */ - public static interface ClassLoaderProvider { - /** - * Returns a class loader. - * - * @param pf a proxy factory that is going to obtain a class loader. - */ - public ClassLoader get(ProxyFactory pf); - } - - /** - * A provider used by createClass() for obtaining - * a class loader. - * get() on this ClassLoaderProvider object - * is called to obtain a class loader. - * - *

The value of this field can be updated for changing the default - * implementation. - * - *

Example: - *

-     * ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
-     *     public ClassLoader get(ProxyFactory pf) {
-     *         return Thread.currentThread().getContextClassLoader();
-     *     }
-     * };
-     * 
- * - * @since 3.4 - */ - public static ClassLoaderProvider classLoaderProvider - = new ClassLoaderProvider() { - public ClassLoader get(ProxyFactory pf) { - return pf.getClassLoader0(); - } - }; - - protected ClassLoader getClassLoader() { - return classLoaderProvider.get(this); - } - - protected ClassLoader getClassLoader0() { - ClassLoader loader = null; - if (superClass != null && !superClass.getName().equals("java.lang.Object")) - loader = superClass.getClassLoader(); - else if (interfaces != null && interfaces.length > 0) - loader = interfaces[0].getClassLoader(); - - if (loader == null) { - loader = getClass().getClassLoader(); - // In case javassist is in the endorsed dir - if (loader == null) { - loader = Thread.currentThread().getContextClassLoader(); - if (loader == null) - loader = ClassLoader.getSystemClassLoader(); - } - } - - return loader; - } - - protected ProtectionDomain getDomain() { - Class clazz; - if (superClass != null && !superClass.getName().equals("java.lang.Object")) - clazz = superClass; - else if (interfaces != null && interfaces.length > 0) - clazz = interfaces[0]; - else - clazz = this.getClass(); - - return clazz.getProtectionDomain(); - } - - /** - * Creates a proxy class and returns an instance of that class. - * - * @param paramTypes parameter types for a constructor. - * @param args arguments passed to a constructor. - * @param mh the method handler for the proxy class. - * @since 3.4 - */ - public Object create(Class[] paramTypes, Object[] args, MethodHandler mh) - throws NoSuchMethodException, IllegalArgumentException, - InstantiationException, IllegalAccessException, InvocationTargetException - { - Object obj = create(paramTypes, args); - ((Proxy)obj).setHandler(mh); - return obj; - } - - /** - * Creates a proxy class and returns an instance of that class. - * - * @param paramTypes parameter types for a constructor. - * @param args arguments passed to a constructor. - */ - public Object create(Class[] paramTypes, Object[] args) - throws NoSuchMethodException, IllegalArgumentException, - InstantiationException, IllegalAccessException, InvocationTargetException - { - Class c = createClass(); - Constructor cons = c.getConstructor(paramTypes); - return cons.newInstance(args); - } - - /** - * Sets the default invocation handler. This invocation handler is shared - * among all the instances of a proxy class unless another is explicitly - * specified. - * @deprecated since 3.12 - * use of this method is incompatible with proxy class caching. - * instead clients should call method {@link Proxy#setHandler(MethodHandler)} to set the handler - * for each newly created proxy instance. - * calling this method will automatically disable caching of classes created by the proxy factory. - */ - public void setHandler(MethodHandler mi) { - // if we were using the cache and the handler is non-null then we must stop caching - if (factoryUseCache && mi != null) { - factoryUseCache = false; - // clear any currently held class so we don't try to reuse it or set its handler field - thisClass = null; - } - handler = mi; - // this retains the behaviour of the old code which resets any class we were holding on to - // this is probably not what is wanted - setField(DEFAULT_INTERCEPTOR, handler); - } - - /** - * A unique class name generator. - */ - public static interface UniqueName { - /** - * Returns a unique class name. - * - * @param classname the super class name of the proxy class. - */ - String get(String classname); - } - - /** - * A unique class name generator. - * Replacing this generator changes the algorithm to generate a - * unique name. The get method does not have to be - * a synchronized method since the access to this field - * is mutually exclusive and thus thread safe. - */ - public static UniqueName nameGenerator = new UniqueName() { - private final String sep = "_$$_jvst" + Integer.toHexString(this.hashCode() & 0xfff) + "_"; - private int counter = 0; - - public String get(String classname) { - return classname + sep + Integer.toHexString(counter++); - } - }; - - private static String makeProxyName(String classname) { - synchronized (nameGenerator) { - return nameGenerator.get(classname); - } - } - - private ClassFile make() throws CannotCompileException { - ClassFile cf = new ClassFile(false, classname, superName); - cf.setAccessFlags(AccessFlag.PUBLIC); - setInterfaces(cf, interfaces, hasGetHandler ? Proxy.class : ProxyObject.class); - ConstPool pool = cf.getConstPool(); - - // legacy: we only add the static field for the default interceptor if caching is disabled - if (!factoryUseCache) { - FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE); - finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); - cf.addField(finfo); - } - - // handler is per instance - FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE); - finfo2.setAccessFlags(AccessFlag.PRIVATE); - cf.addField(finfo2); - - // filter signature is per class - FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE); - finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); - cf.addField(finfo3); - - // the proxy class serial uid must always be a fixed value - FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE); - finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL); - cf.addField(finfo4); - - // HashMap allMethods = getMethods(superClass, interfaces); - // int size = allMethods.size(); - makeConstructors(classname, cf, pool, classname); - - ArrayList forwarders = new ArrayList(); - int s = overrideMethods(cf, pool, classname, forwarders); - addClassInitializer(cf, pool, classname, s, forwarders); - addSetter(classname, cf, pool); - if (!hasGetHandler) - addGetter(classname, cf, pool); - - if (factoryWriteReplace) { - try { - cf.addMethod(makeWriteReplace(pool)); - } - catch (DuplicateMemberException e) { - // writeReplace() is already declared in the super class/interfaces. - } - } - - thisClass = null; - return cf; - } - - private void checkClassAndSuperName() { - if (interfaces == null) - interfaces = new Class[0]; - - if (superClass == null) { - superClass = OBJECT_TYPE; - superName = superClass.getName(); - basename = interfaces.length == 0 ? superName - : interfaces[0].getName(); - } else { - superName = superClass.getName(); - basename = superName; - } - - if (Modifier.isFinal(superClass.getModifiers())) - throw new RuntimeException(superName + " is final"); - - if (basename.startsWith("java.")) - basename = "org.javassist.tmp." + basename; - } - - private void allocateClassName() { - classname = makeProxyName(basename); - } - - private static Comparator sorter = new Comparator() { - - public int compare(Object o1, Object o2) { - Map.Entry e1 = (Map.Entry)o1; - Map.Entry e2 = (Map.Entry)o2; - String key1 = (String)e1.getKey(); - String key2 = (String)e2.getKey(); - return key1.compareTo(key2); - } - }; - - private void makeSortedMethodList() { - checkClassAndSuperName(); - - hasGetHandler = false; // getMethods() may set this to true. - HashMap allMethods = getMethods(superClass, interfaces); - signatureMethods = new ArrayList(allMethods.entrySet()); - Collections.sort(signatureMethods, sorter); - } - - private void computeSignature(MethodFilter filter) // throws CannotCompileException - { - makeSortedMethodList(); - - int l = signatureMethods.size(); - int maxBytes = ((l + 7) >> 3); - signature = new byte[maxBytes]; - for (int idx = 0; idx < l; idx++) - { - Map.Entry e = (Map.Entry)signatureMethods.get(idx); - Method m = (Method)e.getValue(); - int mod = m.getModifiers(); - if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod) - && isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) { - setBit(signature, idx); - } - } - } - - private void installSignature(byte[] signature) // throws CannotCompileException - { - makeSortedMethodList(); - - int l = signatureMethods.size(); - int maxBytes = ((l + 7) >> 3); - if (signature.length != maxBytes) { - throw new RuntimeException("invalid filter signature length for deserialized proxy class"); - } - - this.signature = signature; - } - - private boolean testBit(byte[] signature, int idx) { - int byteIdx = idx >> 3; - if (byteIdx > signature.length) { - return false; - } else { - int bitIdx = idx & 0x7; - int mask = 0x1 << bitIdx; - int sigByte = signature[byteIdx]; - return ((sigByte & mask) != 0); - } - } - - private void setBit(byte[] signature, int idx) { - int byteIdx = idx >> 3; - if (byteIdx < signature.length) { - int bitIdx = idx & 0x7; - int mask = 0x1 << bitIdx; - int sigByte = signature[byteIdx]; - signature[byteIdx] = (byte)(sigByte | mask); - } - } - - private static void setInterfaces(ClassFile cf, Class[] interfaces, Class proxyClass) { - String setterIntf = proxyClass.getName(); - String[] list; - if (interfaces == null || interfaces.length == 0) - list = new String[] { setterIntf }; - else { - list = new String[interfaces.length + 1]; - for (int i = 0; i < interfaces.length; i++) - list[i] = interfaces[i].getName(); - - list[interfaces.length] = setterIntf; - } - - cf.setInterfaces(list); - } - - private static void addClassInitializer(ClassFile cf, ConstPool cp, - String classname, int size, ArrayList forwarders) - throws CannotCompileException - { - FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE); - finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC); - cf.addField(finfo); - MethodInfo minfo = new MethodInfo(cp, "", "()V"); - minfo.setAccessFlags(AccessFlag.STATIC); - setThrows(minfo, cp, new Class[] { ClassNotFoundException.class }); - - Bytecode code = new Bytecode(cp, 0, 2); - code.addIconst(size * 2); - code.addAnewarray("java.lang.reflect.Method"); - final int varArray = 0; - code.addAstore(varArray); - - // forName() must be called here. Otherwise, the class might be - // invisible. - code.addLdc(classname); - code.addInvokestatic("java.lang.Class", - "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); - final int varClass = 1; - code.addAstore(varClass); - - Iterator it = forwarders.iterator(); - while (it.hasNext()) { - Find2MethodsArgs args = (Find2MethodsArgs)it.next(); - callFind2Methods(code, args.methodName, args.delegatorName, - args.origIndex, args.descriptor, varClass, varArray); - } - - code.addAload(varArray); - code.addPutstatic(classname, HOLDER, HOLDER_TYPE); - - code.addLconst(SERIAL_VERSION_UID_VALUE); - code.addPutstatic(classname, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE); - code.addOpcode(Bytecode.RETURN); - minfo.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(minfo); - } - - /** - * @param thisMethod might be null. - */ - private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod, - int index, String desc, int classVar, int arrayVar) { - String findClass = RuntimeSupport.class.getName(); - String findDesc - = "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/reflect/Method;)V"; - - code.addAload(classVar); - code.addLdc(superMethod); - if (thisMethod == null) - code.addOpcode(Opcode.ACONST_NULL); - else - code.addLdc(thisMethod); - - code.addIconst(index); - code.addLdc(desc); - code.addAload(arrayVar); - code.addInvokestatic(findClass, "find2Methods", findDesc); - } - - private static void addSetter(String classname, ClassFile cf, ConstPool cp) - throws CannotCompileException - { - MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER, - HANDLER_SETTER_TYPE); - minfo.setAccessFlags(AccessFlag.PUBLIC); - Bytecode code = new Bytecode(cp, 2, 2); - code.addAload(0); - code.addAload(1); - code.addPutfield(classname, HANDLER, HANDLER_TYPE); - code.addOpcode(Bytecode.RETURN); - minfo.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(minfo); - } - - private static void addGetter(String classname, ClassFile cf, ConstPool cp) - throws CannotCompileException - { - MethodInfo minfo = new MethodInfo(cp, HANDLER_GETTER, - HANDLER_GETTER_TYPE); - minfo.setAccessFlags(AccessFlag.PUBLIC); - Bytecode code = new Bytecode(cp, 1, 1); - code.addAload(0); - code.addGetfield(classname, HANDLER, HANDLER_TYPE); - code.addOpcode(Bytecode.ARETURN); - minfo.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(minfo); - } - - private int overrideMethods(ClassFile cf, ConstPool cp, String className, ArrayList forwarders) - throws CannotCompileException - { - String prefix = makeUniqueName("_d", signatureMethods); - Iterator it = signatureMethods.iterator(); - int index = 0; - while (it.hasNext()) { - Map.Entry e = (Map.Entry)it.next(); - String key = (String)e.getKey(); - Method meth = (Method)e.getValue(); - if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_5 || !isBridge(meth)) - if (testBit(signature, index)) { - override(className, meth, prefix, index, - keyToDesc(key, meth), cf, cp, forwarders); - } - - index++; - } - - return index; - } - - private static boolean isBridge(Method m) { - return m.isBridge(); - } - - private void override(String thisClassname, Method meth, String prefix, - int index, String desc, ClassFile cf, ConstPool cp, ArrayList forwarders) - throws CannotCompileException - { - Class declClass = meth.getDeclaringClass(); - String delegatorName = prefix + index + meth.getName(); - if (Modifier.isAbstract(meth.getModifiers())) - delegatorName = null; - else { - MethodInfo delegator - = makeDelegator(meth, desc, cp, declClass, delegatorName); - // delegator is not a bridge method. See Sec. 15.12.4.5 of JLS 3rd Ed. - delegator.setAccessFlags(delegator.getAccessFlags() & ~AccessFlag.BRIDGE); - cf.addMethod(delegator); - } - - MethodInfo forwarder - = makeForwarder(thisClassname, meth, desc, cp, declClass, - delegatorName, index, forwarders); - cf.addMethod(forwarder); - } - - private void makeConstructors(String thisClassName, ClassFile cf, - ConstPool cp, String classname) throws CannotCompileException - { - Constructor[] cons = SecurityActions.getDeclaredConstructors(superClass); - // legacy: if we are not caching then we need to initialise the default handler - boolean doHandlerInit = !factoryUseCache; - for (int i = 0; i < cons.length; i++) { - Constructor c = cons[i]; - int mod = c.getModifiers(); - if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod) - && isVisible(mod, basename, c)) { - MethodInfo m = makeConstructor(thisClassName, c, cp, superClass, doHandlerInit); - cf.addMethod(m); - } - } - } - - private static String makeUniqueName(String name, List sortedMethods) { - if (makeUniqueName0(name, sortedMethods.iterator())) - return name; - - for (int i = 100; i < 999; i++) { - String s = name + i; - if (makeUniqueName0(s, sortedMethods.iterator())) - return s; - } - - throw new RuntimeException("cannot make a unique method name"); - } - - private static boolean makeUniqueName0(String name, Iterator it) { - while (it.hasNext()) { - Map.Entry e = (Map.Entry)it.next(); - String key = (String)e.getKey(); - if (key.startsWith(name)) - return false; - } - - return true; - } - - /** - * Returns true if the method is visible from the package. - * - * @param mod the modifiers of the method. - */ - private static boolean isVisible(int mod, String from, Member meth) { - if ((mod & Modifier.PRIVATE) != 0) - return false; - else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) - return true; - else { - String p = getPackageName(from); - String q = getPackageName(meth.getDeclaringClass().getName()); - if (p == null) - return q == null; - else - return p.equals(q); - } - } - - private static String getPackageName(String name) { - int i = name.lastIndexOf('.'); - if (i < 0) - return null; - else - return name.substring(0, i); - } - - /* getMethods() may set hasGetHandler to true. - */ - private HashMap getMethods(Class superClass, Class[] interfaceTypes) { - HashMap hash = new HashMap(); - HashSet set = new HashSet(); - for (int i = 0; i < interfaceTypes.length; i++) - getMethods(hash, interfaceTypes[i], set); - - getMethods(hash, superClass, set); - return hash; - } - - private void getMethods(HashMap hash, Class clazz, Set visitedClasses) { - // This both speeds up scanning by avoiding duplicate interfaces and is needed to - // ensure that superinterfaces are always scanned before subinterfaces. - if (!visitedClasses.add(clazz)) - return; - - Class[] ifs = clazz.getInterfaces(); - for (int i = 0; i < ifs.length; i++) - getMethods(hash, ifs[i], visitedClasses); - - Class parent = clazz.getSuperclass(); - if (parent != null) - getMethods(hash, parent, visitedClasses); - - /* Java 5 or later allows covariant return types. - * It also allows contra-variant parameter types - * if a super class is a generics with concrete type arguments - * such as Foo. So the method-overriding rule is complex. - */ - Method[] methods = SecurityActions.getDeclaredMethods(clazz); - for (int i = 0; i < methods.length; i++) - if (!Modifier.isPrivate(methods[i].getModifiers())) { - Method m = methods[i]; - String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m); // see keyToDesc(). - if (key.startsWith(HANDLER_GETTER_KEY)) - hasGetHandler = true; - - // JIRA JASSIST-85 - // put the method to the cache, retrieve previous definition (if any) - Method oldMethod = (Method)hash.put(key, methods[i]); - - // check if visibility has been reduced - if (null != oldMethod && Modifier.isPublic(oldMethod.getModifiers()) - && !Modifier.isPublic(methods[i].getModifiers()) ) { - // we tried to overwrite a public definition with a non-public definition, - // use the old definition instead. - hash.put(key, oldMethod); - } - } - } - - private static final String HANDLER_GETTER_KEY - = HANDLER_GETTER + ":()"; - - private static String keyToDesc(String key, Method m) { - return key.substring(key.indexOf(':') + 1); - } - - private static MethodInfo makeConstructor(String thisClassName, Constructor cons, - ConstPool cp, Class superClass, boolean doHandlerInit) { - String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(), - Void.TYPE); - MethodInfo minfo = new MethodInfo(cp, "", desc); - minfo.setAccessFlags(Modifier.PUBLIC); // cons.getModifiers() & ~Modifier.NATIVE - setThrows(minfo, cp, cons.getExceptionTypes()); - Bytecode code = new Bytecode(cp, 0, 0); - - // legacy: if we are not using caching then we initialise the instance's handler - // from the class's static default interceptor and skip the next few instructions if - // it is non-null - if (doHandlerInit) { - code.addAload(0); - code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE); - code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE); - code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE); - code.addOpcode(Opcode.IFNONNULL); - code.addIndex(10); - } - // if caching is enabled then we don't have a handler to initialise so this else branch will install - // the handler located in the static field of class RuntimeSupport. - code.addAload(0); - code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR, HANDLER_TYPE); - code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE); - int pc = code.currentPc(); - - code.addAload(0); - int s = addLoadParameters(code, cons.getParameterTypes(), 1); - code.addInvokespecial(superClass.getName(), "", desc); - code.addOpcode(Opcode.RETURN); - code.setMaxLocals(s + 1); - CodeAttribute ca = code.toCodeAttribute(); - minfo.setCodeAttribute(ca); - - StackMapTable.Writer writer = new StackMapTable.Writer(32); - writer.sameFrame(pc); - ca.setAttribute(writer.toStackMapTable(cp)); - return minfo; - } - - private static MethodInfo makeDelegator(Method meth, String desc, - ConstPool cp, Class declClass, String delegatorName) { - MethodInfo delegator = new MethodInfo(cp, delegatorName, desc); - delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC - | (meth.getModifiers() & ~(Modifier.PRIVATE - | Modifier.PROTECTED - | Modifier.ABSTRACT - | Modifier.NATIVE - | Modifier.SYNCHRONIZED))); - setThrows(delegator, cp, meth); - Bytecode code = new Bytecode(cp, 0, 0); - code.addAload(0); - int s = addLoadParameters(code, meth.getParameterTypes(), 1); - code.addInvokespecial(declClass.getName(), meth.getName(), desc); - addReturn(code, meth.getReturnType()); - code.setMaxLocals(++s); - delegator.setCodeAttribute(code.toCodeAttribute()); - return delegator; - } - - /** - * @param delegatorName null if the original method is abstract. - */ - private static MethodInfo makeForwarder(String thisClassName, - Method meth, String desc, ConstPool cp, - Class declClass, String delegatorName, int index, - ArrayList forwarders) { - MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc); - forwarder.setAccessFlags(Modifier.FINAL - | (meth.getModifiers() & ~(Modifier.ABSTRACT - | Modifier.NATIVE - | Modifier.SYNCHRONIZED))); - setThrows(forwarder, cp, meth); - int args = Descriptor.paramSize(desc); - Bytecode code = new Bytecode(cp, 0, args + 2); - /* - * static { - * methods[index * 2] - * = RuntimeSupport.findSuperMethod(this, , ); - * methods[index * 2 + 1] - * = RuntimeSupport.findMethod(this, , ); - * or = null // the original method is abstract. - * } - * : - * return ($r)handler.invoke(this, methods[index * 2], - * methods[index * 2 + 1], $args); - */ - int origIndex = index * 2; - int delIndex = index * 2 + 1; - int arrayVar = args + 1; - code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE); - code.addAstore(arrayVar); - - forwarders.add(new Find2MethodsArgs(meth.getName(), delegatorName, desc, origIndex)); - - code.addAload(0); - code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE); - code.addAload(0); - - code.addAload(arrayVar); - code.addIconst(origIndex); - code.addOpcode(Opcode.AALOAD); - - code.addAload(arrayVar); - code.addIconst(delIndex); - code.addOpcode(Opcode.AALOAD); - - makeParameterList(code, meth.getParameterTypes()); - code.addInvokeinterface(MethodHandler.class.getName(), "invoke", - "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", - 5); - Class retType = meth.getReturnType(); - addUnwrapper(code, retType); - addReturn(code, retType); - - CodeAttribute ca = code.toCodeAttribute(); - forwarder.setCodeAttribute(ca); - return forwarder; - } - - static class Find2MethodsArgs { - String methodName, delegatorName, descriptor; - int origIndex; - - Find2MethodsArgs(String mname, String dname, String desc, int index) { - methodName = mname; - delegatorName = dname; - descriptor = desc; - origIndex = index; - } - } - - private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) { - Class[] exceptions = orig.getExceptionTypes(); - setThrows(minfo, cp, exceptions); - } - - private static void setThrows(MethodInfo minfo, ConstPool cp, - Class[] exceptions) { - if (exceptions.length == 0) - return; - - String[] list = new String[exceptions.length]; - for (int i = 0; i < exceptions.length; i++) - list[i] = exceptions[i].getName(); - - ExceptionsAttribute ea = new ExceptionsAttribute(cp); - ea.setExceptions(list); - minfo.setExceptionsAttribute(ea); - } - - private static int addLoadParameters(Bytecode code, Class[] params, - int offset) { - int stacksize = 0; - int n = params.length; - for (int i = 0; i < n; ++i) - stacksize += addLoad(code, stacksize + offset, params[i]); - - return stacksize; - } - - private static int addLoad(Bytecode code, int n, Class type) { - if (type.isPrimitive()) { - if (type == Long.TYPE) { - code.addLload(n); - return 2; - } - else if (type == Float.TYPE) - code.addFload(n); - else if (type == Double.TYPE) { - code.addDload(n); - return 2; - } - else - code.addIload(n); - } - else - code.addAload(n); - - return 1; - } - - private static int addReturn(Bytecode code, Class type) { - if (type.isPrimitive()) { - if (type == Long.TYPE) { - code.addOpcode(Opcode.LRETURN); - return 2; - } - else if (type == Float.TYPE) - code.addOpcode(Opcode.FRETURN); - else if (type == Double.TYPE) { - code.addOpcode(Opcode.DRETURN); - return 2; - } - else if (type == Void.TYPE) { - code.addOpcode(Opcode.RETURN); - return 0; - } - else - code.addOpcode(Opcode.IRETURN); - } - else - code.addOpcode(Opcode.ARETURN); - - return 1; - } - - private static void makeParameterList(Bytecode code, Class[] params) { - int regno = 1; - int n = params.length; - code.addIconst(n); - code.addAnewarray("java/lang/Object"); - for (int i = 0; i < n; i++) { - code.addOpcode(Opcode.DUP); - code.addIconst(i); - Class type = params[i]; - if (type.isPrimitive()) - regno = makeWrapper(code, type, regno); - else { - code.addAload(regno); - regno++; - } - - code.addOpcode(Opcode.AASTORE); - } - } - - private static int makeWrapper(Bytecode code, Class type, int regno) { - int index = FactoryHelper.typeIndex(type); - String wrapper = FactoryHelper.wrapperTypes[index]; - code.addNew(wrapper); - code.addOpcode(Opcode.DUP); - addLoad(code, regno, type); - code.addInvokespecial(wrapper, "", - FactoryHelper.wrapperDesc[index]); - return regno + FactoryHelper.dataSize[index]; - } - - private static void addUnwrapper(Bytecode code, Class type) { - if (type.isPrimitive()) { - if (type == Void.TYPE) - code.addOpcode(Opcode.POP); - else { - int index = FactoryHelper.typeIndex(type); - String wrapper = FactoryHelper.wrapperTypes[index]; - code.addCheckcast(wrapper); - code.addInvokevirtual(wrapper, - FactoryHelper.unwarpMethods[index], - FactoryHelper.unwrapDesc[index]); - } - } - else - code.addCheckcast(type.getName()); - } - - private static MethodInfo makeWriteReplace(ConstPool cp) { - MethodInfo minfo = new MethodInfo(cp, "writeReplace", "()Ljava/lang/Object;"); - String[] list = new String[1]; - list[0] = "java.io.ObjectStreamException"; - ExceptionsAttribute ea = new ExceptionsAttribute(cp); - ea.setExceptions(list); - minfo.setExceptionsAttribute(ea); - Bytecode code = new Bytecode(cp, 0, 1); - code.addAload(0); - code.addInvokestatic("javassist.util.proxy.RuntimeSupport", - "makeSerializedProxy", - "(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;"); - code.addOpcode(Opcode.ARETURN); - minfo.setCodeAttribute(code.toCodeAttribute()); - return minfo; - } -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java b/src/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java deleted file mode 100644 index 59f8f8b..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -/** - * The interface implemented by proxy classes. - * This interface is available only if the super class of the proxy object - * does not have a getHandler() method. If the super class - * has getHandler, then Proxy interface is - * available. - * - * @see ProxyFactory - * @see Proxy - */ -public interface ProxyObject extends Proxy { - /** - * Sets a handler. It can be used for changing handlers - * during runtime. - */ - void setHandler(MethodHandler mi); - - /** - * Get the handler. - * This can be used to access the underlying MethodHandler - * or to serialize it properly. - * - * @see ProxyFactory#getHandler(Proxy) - */ - MethodHandler getHandler(); -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java b/src/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java deleted file mode 100644 index ee81e88..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectStreamClass; - -/** - * An input stream class which knows how to deserialize proxies created via {@link ProxyFactory} and - * serializedo via a {@link ProxyObjectOutputStream}. It must be used when deserialising proxies created - * from a proxy factory configured with {@link ProxyFactory#useWriteReplace} set to false. - * - * @author Andrew Dinn - */ -public class ProxyObjectInputStream extends ObjectInputStream -{ - /** - * create an input stream which can be used to deserialize an object graph which includes proxies created - * using class ProxyFactory. the classloader used to resolve proxy superclass and interface names - * read from the input stream will default to the current thread's context class loader or the system - * classloader if the context class loader is null. - * @param in - * @throws java.io.StreamCorruptedException whenever ObjectInputStream would also do so - * @throws IOException whenever ObjectInputStream would also do so - * @throws SecurityException whenever ObjectInputStream would also do so - * @throws NullPointerException if in is null - */ - public ProxyObjectInputStream(InputStream in) throws IOException - { - super(in); - loader = Thread.currentThread().getContextClassLoader(); - if (loader == null) { - loader = ClassLoader.getSystemClassLoader(); - } - } - - /** - * Reset the loader to be - * @param loader - */ - public void setClassLoader(ClassLoader loader) - { - if (loader != null) { - this.loader = loader; - } else { - loader = ClassLoader.getSystemClassLoader(); - } - } - - protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { - boolean isProxy = readBoolean(); - if (isProxy) { - String name = (String)readObject(); - Class superClass = loader.loadClass(name); - int length = readInt(); - Class[] interfaces = new Class[length]; - for (int i = 0; i < length; i++) { - name = (String)readObject(); - interfaces[i] = loader.loadClass(name); - } - length = readInt(); - byte[] signature = new byte[length]; - read(signature); - ProxyFactory factory = new ProxyFactory(); - // we must always use the cache and never use writeReplace when using - // ProxyObjectOutputStream and ProxyObjectInputStream - factory.setUseCache(true); - factory.setUseWriteReplace(false); - factory.setSuperclass(superClass); - factory.setInterfaces(interfaces); - Class proxyClass = factory.createClass(signature); - return ObjectStreamClass.lookup(proxyClass); - } else { - return super.readClassDescriptor(); - } - } - - /** - * the loader to use to resolve classes for proxy superclass and interface names read - * from the stream. defaults to the context class loader of the thread which creates - * the input stream or the system class loader if the context class loader is null. - */ - private ClassLoader loader; -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java b/src/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java deleted file mode 100644 index 84ac6cb..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamClass; -import java.io.OutputStream; - -/** - * An input stream class which knows how to serialize proxies created via {@link ProxyFactory}. It must - * be used when serialising proxies created from a proxy factory configured with - * {@link ProxyFactory#useWriteReplace} set to false. Subsequent deserialization of the serialized data - * must employ a {@link ProxyObjectInputStream} - * - * @author Andrew Dinn - */ -public class ProxyObjectOutputStream extends ObjectOutputStream -{ - /** - * create an output stream which can be used to serialize an object graph which includes proxies created - * using class ProxyFactory - * @param out - * @throws IOException whenever ObjectOutputStream would also do so - * @throws SecurityException whenever ObjectOutputStream would also do so - * @throws NullPointerException if out is null - */ - public ProxyObjectOutputStream(OutputStream out) throws IOException - { - super(out); - } - - protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { - Class cl = desc.forClass(); - if (ProxyFactory.isProxyClass(cl)) { - writeBoolean(true); - Class superClass = cl.getSuperclass(); - Class[] interfaces = cl.getInterfaces(); - byte[] signature = ProxyFactory.getFilterSignature(cl); - String name = superClass.getName(); - writeObject(name); - // we don't write the marker interface ProxyObject - writeInt(interfaces.length - 1); - for (int i = 0; i < interfaces.length; i++) { - Class interfaze = interfaces[i]; - if (interfaze != ProxyObject.class && interfaze != Proxy.class) { - name = interfaces[i].getName(); - writeObject(name); - } - } - writeInt(signature.length); - write(signature); - } else { - writeBoolean(false); - super.writeClassDescriptor(desc); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java b/src/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java deleted file mode 100644 index 9f4bbf5..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -import java.lang.reflect.Method; -import java.io.Serializable; - -/** - * Runtime support routines that the classes generated by ProxyFactory use. - * - * @see ProxyFactory - */ -public class RuntimeSupport { - /** - * A method handler that only executes a method. - */ - public static MethodHandler default_interceptor = new DefaultMethodHandler(); - - static class DefaultMethodHandler implements MethodHandler, Serializable { - public Object invoke(Object self, Method m, - Method proceed, Object[] args) - throws Exception - { - return proceed.invoke(self, args); - } - }; - - /** - * Finds two methods specified by the parameters and stores them - * into the given array. - * - * @throws RuntimeException if the methods are not found. - * @see javassist.util.proxy.ProxyFactory - */ - public static void find2Methods(Class clazz, String superMethod, - String thisMethod, int index, - String desc, java.lang.reflect.Method[] methods) - { - methods[index + 1] = thisMethod == null ? null - : findMethod(clazz, thisMethod, desc); - methods[index] = findSuperClassMethod(clazz, superMethod, desc); - } - - /** - * Finds two methods specified by the parameters and stores them - * into the given array. - * - *

Added back for JBoss Seam. See JASSIST-206.

- * - * @throws RuntimeException if the methods are not found. - * @see javassist.util.proxy.ProxyFactory - * @deprecated replaced by {@link #find2Methods(Class, String, String, int, String, Method[])} - */ - public static void find2Methods(Object self, String superMethod, - String thisMethod, int index, - String desc, java.lang.reflect.Method[] methods) - { - methods[index + 1] = thisMethod == null ? null - : findMethod(self, thisMethod, desc); - methods[index] = findSuperMethod(self, superMethod, desc); - } - - /** - * Finds a method with the given name and descriptor. - * It searches only the class of self. - * - *

Added back for JBoss Seam. See JASSIST-206.

- * - * @throws RuntimeException if the method is not found. - * @deprecated replaced by {@link #findMethod(Class, String, String)} - */ - public static Method findMethod(Object self, String name, String desc) { - Method m = findMethod2(self.getClass(), name, desc); - if (m == null) - error(self.getClass(), name, desc); - - return m; - } - - /** - * Finds a method with the given name and descriptor. - * It searches only the class of self. - * - * @throws RuntimeException if the method is not found. - */ - public static Method findMethod(Class clazz, String name, String desc) { - Method m = findMethod2(clazz, name, desc); - if (m == null) - error(clazz, name, desc); - - return m; - } - - /** - * Finds a method that has the given name and descriptor and is declared - * in the super class. - * - * @throws RuntimeException if the method is not found. - */ - public static Method findSuperMethod(Object self, String name, String desc) { - // for JBoss Seam. See JASSIST-183. - Class clazz = self.getClass(); - return findSuperClassMethod(clazz, name, desc); - } - - /** - * Finds a method that has the given name and descriptor and is declared - * in the super class. - * - * @throws RuntimeException if the method is not found. - */ - public static Method findSuperClassMethod(Class clazz, String name, String desc) { - Method m = findSuperMethod2(clazz.getSuperclass(), name, desc); - if (m == null) - m = searchInterfaces(clazz, name, desc); - - if (m == null) - error(clazz, name, desc); - - return m; - } - - private static void error(Class clazz, String name, String desc) { - throw new RuntimeException("not found " + name + ":" + desc - + " in " + clazz.getName()); - } - - private static Method findSuperMethod2(Class clazz, String name, String desc) { - Method m = findMethod2(clazz, name, desc); - if (m != null) - return m; - - Class superClass = clazz.getSuperclass(); - if (superClass != null) { - m = findSuperMethod2(superClass, name, desc); - if (m != null) - return m; - } - - return searchInterfaces(clazz, name, desc); - } - - private static Method searchInterfaces(Class clazz, String name, String desc) { - Method m = null; - Class[] interfaces = clazz.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - m = findSuperMethod2(interfaces[i], name, desc); - if (m != null) - return m; - } - - return m; - } - - private static Method findMethod2(Class clazz, String name, String desc) { - Method[] methods = SecurityActions.getDeclaredMethods(clazz); - int n = methods.length; - for (int i = 0; i < n; i++) - if (methods[i].getName().equals(name) - && makeDescriptor(methods[i]).equals(desc)) - return methods[i]; - - return null; - } - - /** - * Makes a descriptor for a given method. - */ - public static String makeDescriptor(Method m) { - Class[] params = m.getParameterTypes(); - return makeDescriptor(params, m.getReturnType()); - } - - /** - * Makes a descriptor for a given method. - * - * @param params parameter types. - * @param retType return type. - */ - public static String makeDescriptor(Class[] params, Class retType) { - StringBuffer sbuf = new StringBuffer(); - sbuf.append('('); - for (int i = 0; i < params.length; i++) - makeDesc(sbuf, params[i]); - - sbuf.append(')'); - if (retType != null) - makeDesc(sbuf, retType); - - return sbuf.toString(); - } - - /** - * Makes a descriptor for a given method. - * - * @param params the descriptor of parameter types. - * @param retType return type. - */ - public static String makeDescriptor(String params, Class retType) { - StringBuffer sbuf = new StringBuffer(params); - makeDesc(sbuf, retType); - return sbuf.toString(); - } - - private static void makeDesc(StringBuffer sbuf, Class type) { - if (type.isArray()) { - sbuf.append('['); - makeDesc(sbuf, type.getComponentType()); - } - else if (type.isPrimitive()) { - if (type == Void.TYPE) - sbuf.append('V'); - else if (type == Integer.TYPE) - sbuf.append('I'); - else if (type == Byte.TYPE) - sbuf.append('B'); - else if (type == Long.TYPE) - sbuf.append('J'); - else if (type == Double.TYPE) - sbuf.append('D'); - else if (type == Float.TYPE) - sbuf.append('F'); - else if (type == Character.TYPE) - sbuf.append('C'); - else if (type == Short.TYPE) - sbuf.append('S'); - else if (type == Boolean.TYPE) - sbuf.append('Z'); - else - throw new RuntimeException("bad type: " + type.getName()); - } - else - sbuf.append('L').append(type.getName().replace('.', '/')) - .append(';'); - } - - /** - * Converts a proxy object to an object that is writable to an - * object stream. This method is called by writeReplace() - * in a proxy class. - * - * @since 3.4 - */ - public static SerializedProxy makeSerializedProxy(Object proxy) - throws java.io.InvalidClassException - { - Class clazz = proxy.getClass(); - - MethodHandler methodHandler = null; - if (proxy instanceof ProxyObject) - methodHandler = ((ProxyObject)proxy).getHandler(); - else if (proxy instanceof Proxy) - methodHandler = ProxyFactory.getHandler((Proxy)proxy); - - return new SerializedProxy(clazz, ProxyFactory.getFilterSignature(clazz), methodHandler); - } -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java b/src/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java deleted file mode 100644 index d8e8a77..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ -package com.wenshuo.agent.javassist.util.proxy; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; - -class SecurityActions { - static Method[] getDeclaredMethods(final Class clazz) { - if (System.getSecurityManager() == null) - return clazz.getDeclaredMethods(); - else { - return (Method[]) AccessController - .doPrivileged(new PrivilegedAction() { - public Object run() { - return clazz.getDeclaredMethods(); - } - }); - } - } - - static Constructor[] getDeclaredConstructors(final Class clazz) { - if (System.getSecurityManager() == null) - return clazz.getDeclaredConstructors(); - else { - return (Constructor[]) AccessController - .doPrivileged(new PrivilegedAction() { - public Object run() { - return clazz.getDeclaredConstructors(); - } - }); - } - } - - static Method getDeclaredMethod(final Class clazz, final String name, - final Class[] types) throws NoSuchMethodException { - if (System.getSecurityManager() == null) - return clazz.getDeclaredMethod(name, types); - else { - try { - return (Method) AccessController - .doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - return clazz.getDeclaredMethod(name, types); - } - }); - } - catch (PrivilegedActionException e) { - if (e.getCause() instanceof NoSuchMethodException) - throw (NoSuchMethodException) e.getCause(); - - throw new RuntimeException(e.getCause()); - } - } - } - - static Constructor getDeclaredConstructor(final Class clazz, - final Class[] types) - throws NoSuchMethodException - { - if (System.getSecurityManager() == null) - return clazz.getDeclaredConstructor(types); - else { - try { - return (Constructor) AccessController - .doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - return clazz.getDeclaredConstructor(types); - } - }); - } - catch (PrivilegedActionException e) { - if (e.getCause() instanceof NoSuchMethodException) - throw (NoSuchMethodException) e.getCause(); - - throw new RuntimeException(e.getCause()); - } - } - } - - static void setAccessible(final AccessibleObject ao, - final boolean accessible) { - if (System.getSecurityManager() == null) - ao.setAccessible(accessible); - else { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - ao.setAccessible(accessible); - return null; - } - }); - } - } - - static void set(final Field fld, final Object target, final Object value) - throws IllegalAccessException - { - if (System.getSecurityManager() == null) - fld.set(target, value); - else { - try { - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - fld.set(target, value); - return null; - } - }); - } - catch (PrivilegedActionException e) { - if (e.getCause() instanceof NoSuchMethodException) - throw (IllegalAccessException) e.getCause(); - - throw new RuntimeException(e.getCause()); - } - } - } -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java b/src/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java deleted file mode 100644 index 70e6469..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later, - * or the Apache License Version 2.0. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package com.wenshuo.agent.javassist.util.proxy; - -import java.io.Serializable; -import java.io.ObjectStreamException; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.security.ProtectionDomain; - -/** - * A proxy object is converted into an instance of this class - * when it is written to an output stream. - * - * @see RuntimeSupport#makeSerializedProxy(Object) - */ -class SerializedProxy implements Serializable { - private String superClass; - private String[] interfaces; - private byte[] filterSignature; - private MethodHandler handler; - - SerializedProxy(Class proxy, byte[] sig, MethodHandler h) { - filterSignature = sig; - handler = h; - superClass = proxy.getSuperclass().getName(); - Class[] infs = proxy.getInterfaces(); - int n = infs.length; - interfaces = new String[n - 1]; - String setterInf = ProxyObject.class.getName(); - String setterInf2 = Proxy.class.getName(); - for (int i = 0; i < n; i++) { - String name = infs[i].getName(); - if (!name.equals(setterInf) && !name.equals(setterInf2)) - interfaces[i] = name; - } - } - - /** - * Load class. - * - * @param className the class name - * @return loaded class - * @throws ClassNotFoundException for any error - */ - protected Class loadClass(final String className) throws ClassNotFoundException { - try { - return (Class)AccessController.doPrivileged(new PrivilegedExceptionAction(){ - public Object run() throws Exception{ - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - return Class.forName(className, true, cl); - } - }); - } - catch (PrivilegedActionException pae) { - throw new RuntimeException("cannot load the class: " + className, pae.getException()); - } - } - - Object readResolve() throws ObjectStreamException { - try { - int n = interfaces.length; - Class[] infs = new Class[n]; - for (int i = 0; i < n; i++) - infs[i] = loadClass(interfaces[i]); - - ProxyFactory f = new ProxyFactory(); - f.setSuperclass(loadClass(superClass)); - f.setInterfaces(infs); - Proxy proxy = (Proxy)f.createClass(filterSignature).newInstance(); - proxy.setHandler(handler); - return proxy; - } - catch (ClassNotFoundException e) { - throw new java.io.InvalidClassException(e.getMessage()); - } - catch (InstantiationException e2) { - throw new java.io.InvalidObjectException(e2.getMessage()); - } - catch (IllegalAccessException e3) { - throw new java.io.InvalidClassException(e3.getMessage()); - } - } -} diff --git a/src/com/wenshuo/agent/javassist/util/proxy/package.html b/src/com/wenshuo/agent/javassist/util/proxy/package.html deleted file mode 100644 index 6c77804..0000000 --- a/src/com/wenshuo/agent/javassist/util/proxy/package.html +++ /dev/null @@ -1,6 +0,0 @@ - - -Dynamic proxy (similar to Enhancer of cglib). -See ProxyFactory for more details. - - diff --git a/src/com/wenshuo/agent/log/ExecuteLogUtils.java b/src/com/wenshuo/agent/log/ExecuteLogUtils.java deleted file mode 100644 index c1f9678..0000000 --- a/src/com/wenshuo/agent/log/ExecuteLogUtils.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * @(#)ExecuteLogUtils.java 2015-7-27 下午05:57:01 javaagent Copyright 2015 wenshuo, Inc. All rights reserved. wenshuo - * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ -package com.wenshuo.agent.log; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.RandomAccessFile; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import com.wenshuo.agent.AgentUtils; -import com.wenshuo.agent.ConfigUtils; -import com.wenshuo.agent.NamedThreadFactory; - -/** - * ExecuteLogUtils - * - * @author dingjsh - * @time 2015-7-27下午05:57:01 - */ -public class ExecuteLogUtils { - - private static BufferedWriter counterLogWriter = null; - - private static long nextDayStartTimeMillis; - - private static String logFileName = null; - - private static long startTimeMillis; - - private static ScheduledThreadPoolExecutor counterLogExecutor; - - private static ConcurrentHashMap> executeCounterMap; - - private static final Object executeCounterLock = new Object(); - - private static final int BUFFER_SIZE = 256 * 1024; - - private static final String ENCODING = "UTF-8"; - - private static boolean isUsingNanoTime = false; - - private static boolean logAvgExecuteTime = false; - - private static volatile boolean isInitialized = false; - - private ExecuteLogUtils() { - super(); - } - - /** - * 初使化 - * - * @author dingjsh - * @time 2015-7-30上午10:47:58 - */ - public static synchronized void init() { - if (isInitialized) { - return; - } - logFileName = ConfigUtils.getLogFileName(); - int interval = ConfigUtils.getLogInterval(); - logAvgExecuteTime = ConfigUtils.isLogAvgExecuteTime(); - isUsingNanoTime = ConfigUtils.isUsingNanoTime(); - if (AgentUtils.isBlank(logFileName)) { - System.err.println("日志文件名为空"); - throw new RuntimeException("日志文件名为空"); - } - setNextDateStartTimeMillis(); - initWriter(); - executeCounterMap = new ConcurrentHashMap>(); - startTimeMillis = System.currentTimeMillis(); - counterLogExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("pool-thread-agent-log", true)); - counterLogExecutor.scheduleWithFixedDelay(new OutputLogRunnable(), interval, interval, TimeUnit.SECONDS); - isInitialized = true; - } - - public static void log(String className, String methodName, long executeTime) { - logExecuteCounter(className, methodName, executeTime); - } - - /** - * 输出方法执行记数日志 - * - * @author dingjsh - * @time 2015-7-28下午01:35:25 - */ - synchronized static void outputCounterLog() throws IOException { - ConcurrentHashMap> executeCounterMapInner = executeCounterMap; - executeCounterMap = new ConcurrentHashMap>(); - String startTime = formatTimeMillis(startTimeMillis); - String endTime = formatTimeMillis(System.currentTimeMillis()); - long byteLength = removeJSONArrayEndBracket(); - if (0 == byteLength) { - writeLog("[", true, startTimeMillis); - } - - Set>> entrySet = executeCounterMapInner.entrySet(); - Iterator>> ite = entrySet.iterator(); - int length = entrySet.size(); - if (length > 0 && byteLength > 10) { // 说明文件不只是[],json数组中已经有内容 - writeLog(",", startTimeMillis); - } - for (int index = 0; ite.hasNext(); index++) { - Map.Entry> entry = ite.next(); - String className = entry.getKey(); - Map method2ExecuteMap = entry.getValue(); - String methodExecuteJson = MethodExecuteJSONformatter.getMethodExecuteJSON(className, method2ExecuteMap, - startTime, endTime, isUsingNanoTime, logAvgExecuteTime); - writeLog(methodExecuteJson, startTimeMillis); - if (index < length - 1) { - writeLog(",", true, startTimeMillis); - } - } - writeLog("]", true, startTimeMillis); - flushLogAndClose(); - startTimeMillis = System.currentTimeMillis(); - } - - private static void logExecuteCounter(String className, String methodName, long executeTime) { - // dingjs modified in 20210825 原来是用锁和AtomicLong来避免线程安全问题,现在去掉这些实现。原因是此处线程安全问题顶多导致 - // 数据有一些误差,这个误差对于agent数据监控并没有什么影响,牺牲一定的准确性带来性能的提升是有必要的 - ConcurrentHashMap methodCounterMap = getOrCreateClassExecutesMapping(className); - long[] counter = methodCounterMap.get(methodName); - if (null == counter) { - methodCounterMap.put(methodName, new long[]{1, executeTime}); - } else { - counter[0]++; - counter[1] = executeTime + counter[1]; - } - } - - /** - * ExecuteLogUtils - * - * @param className 类名 - * @return class和它的调用次数映射关系 - * @description 获得class和它的调用次数映射关系,如果exeuteCounterMap中没有,则创建一个并放入 - * @author dingjsh - * @date 2018年5月25日 下午4:40:33 - * @version 1.2.0 - */ - private static ConcurrentHashMap getOrCreateClassExecutesMapping(String className) { - ConcurrentHashMap methodCounterMap = executeCounterMap.get(className); - if (null == methodCounterMap) { - synchronized (executeCounterLock) { - methodCounterMap = executeCounterMap.get(className); - if (null == methodCounterMap) { - methodCounterMap = new ConcurrentHashMap(); - executeCounterMap.put(className, methodCounterMap); - } - } - } - return methodCounterMap; - } - - private static void writeLog(String logValue, long currTimeMillis) { - writeLog(logValue, false, currTimeMillis); - } - - private static void writeLog(String logValue, boolean newLine, long currTimeMillis) { - ensureLogFileUpToDate(currTimeMillis); - try { - counterLogWriter.write(logValue); - if (newLine) { - counterLogWriter.newLine(); - } - } catch (IOException e) { - System.err.println(e); - } - - } - - private static void flushLogAndClose() { - try { - counterLogWriter.flush(); - } catch (IOException e) { - System.err.println(e); - } finally { - AgentUtils.closeQuietly(counterLogWriter); - counterLogWriter = null; - } - } - - /** - * 确保日志文件没过时,日志文件都会加上日期后缀,如果当前日志文件 - */ - private static void ensureLogFileUpToDate(long currTimeMillis) { - if (currTimeMillis >= nextDayStartTimeMillis) { - try { - if (null != counterLogWriter) { - counterLogWriter.flush(); - } - } catch (Exception e) { - System.err.println(e); - } finally { - AgentUtils.closeQuietly(counterLogWriter); - } - initWriter(); - setNextDateStartTimeMillis(); - } - if (null == counterLogWriter) { - initWriter(); - } - - } - - private static void initWriter() { - try { - File logFile = getCounterLogFile(logFileName); - counterLogWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile, true), ENCODING), - BUFFER_SIZE); - } catch (IOException e) { - System.err.println(e); - throw new RuntimeException("无法初始化【" + logFileName + "】,建议您检查磁盘空间,或者手动删除该日志文件"); - } - } - - private static File getCounterLogFile(String logFileName) throws IOException { - - String logFileNameWithDate = getCurrDateString(); - int lastIndexOfDot = logFileName.lastIndexOf('.'); - if (lastIndexOfDot > 0) { - logFileName = logFileName.substring(0, lastIndexOfDot) + "." + logFileNameWithDate - + logFileName.substring(lastIndexOfDot); - } else { - logFileName = logFileName + "." + logFileNameWithDate + ".log"; - } - File logFile = getLogFile(logFileName); - return logFile; - } - - private static File getLogFile(String logFileName) throws IOException { - File logFile = new File(logFileName); - if (logFile.isDirectory()) { - System.err.println("【" + logFileName + "】是文件夹"); - throw new RuntimeException("【" + logFileName + "】是文件夹"); - } else if (!logFile.exists()) { - // dingjs modified in 20140513 - File dirFile = logFile.getParentFile(); - AgentUtils.forceMkdir(dirFile); - boolean created = logFile.createNewFile(); - if (!created) { - System.err.println("【" + logFileName + "】创建失败"); - throw new RuntimeException("【" + logFileName + "】创建失败"); - } - } - return logFile; - } - - private static String getCurrDateString() { - Date today = new Date(); - return new SimpleDateFormat("yyyy-MM-dd").format(today); - } - - /** - * 由开始日期转换为开始时间,一般在以时间为条件的查询中使用 - */ - private static Date date2StartTime(Date date) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); - String dateStr = sdf.format(date); - Date startTime = null; - try { - startTime = sdf.parse(dateStr + " 00:00:00"); - } catch (ParseException e) { - System.err.println(e); - } - return startTime; - } - - /** - * 设置下一天的开始时间 - */ - private static void setNextDateStartTimeMillis() { - Date currDate = new Date(); - Calendar cal = Calendar.getInstance(); - cal.setTime(currDate); - cal.add(Calendar.DATE, 1); - Date startTime = date2StartTime(cal.getTime()); - nextDayStartTimeMillis = startTime.getTime(); - } - - private static String formatTimeMillis(long timeMillis) { - Date date = new Date(timeMillis); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - return sdf.format(date); - } - - private static long removeJSONArrayEndBracket() throws IOException { - File logFile = getCounterLogFile(logFileName); - RandomAccessFile f = new RandomAccessFile(logFile.getAbsolutePath(), "rw"); - long length = f.length(); - byte b = -1; - while (b != 93 && length > 0) { - length -= 1; - f.seek(length); - b = f.readByte(); - } - f.setLength(length); - f.close(); - return length; - } -} \ No newline at end of file diff --git a/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java b/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java deleted file mode 100644 index 13e1454..0000000 --- a/src/com/wenshuo/agent/log/MethodExecuteJSONformatter.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.wenshuo.agent.log; - -import java.util.Map; - - -/** - * MethodExecuteJSONformatter - * - * @author dingjsh - * @version 2.0.0 - * @description 2.0.0 - * @date 2018年8月22日 下午8:07:01 - */ -class MethodExecuteJSONformatter { - - /** - * MethodExecuteJSONformatter - * - * @param className 类名 - * @param method2ExecuteMap Map<方法名, Long[]> 数组第一个是执行次数,第二个是执行时间 - * @param startTime 统计周期的开始时间,格式为 yyyy-MM-dd HH:mm:ss - * @param endTime 统计周期的结束时间,格式为 yyyy-MM-dd HH:mm:ss - * @return 格式化后的json string - * @description 将方法执行监控数据格式化为json string - * @author dingjsh - * @date 2018年8月22日 下午8:49:18 - * @version 2.0.0 - */ - static String getMethodExecuteJSON(String className, Map method2ExecuteMap, - String startTime, String endTime, boolean isUsingNanoTime, boolean logAvgExecuteTime) { - StringBuilder json = new StringBuilder("{"); - appendString(json, "class", className).append(","); - appendString(json, "start", startTime).append(","); - appendString(json, "end", endTime).append(","); - appendKey(json, "methods").append("["); - for (Map.Entry methodEntry : method2ExecuteMap.entrySet()) { - String methodName = methodEntry.getKey(); - long[] executeCounter = methodEntry.getValue(); - long counter = executeCounter[0]; - long timeInMillis - = isUsingNanoTime ? executeCounter[1] / 1000000 : executeCounter[1]; - json.append("{"); - appendString(json, "name", methodName).append(","); - appendLong(json, "counter", counter).append(","); - appendLong(json, "time", timeInMillis); - if (logAvgExecuteTime && counter > 0) { - json.append(","); - appendLong(json, "avg", timeInMillis / counter); - } - json.append("},"); - } - if (json.charAt(json.length() - 1) == ',') { - json.deleteCharAt(json.length() - 1); - } - json.append("]}"); - return json.toString(); - } - - private static StringBuilder appendString(StringBuilder jsonBuilder, String key, String value) { - return appendKey(jsonBuilder, key).append("\"").append(value).append("\""); - } - - private static StringBuilder appendLong(StringBuilder jsonBuilder, String key, long value) { - return appendKey(jsonBuilder, key).append(value); - } - - private static StringBuilder appendKey(StringBuilder jsonBuilder, String key) { - return jsonBuilder.append("\"").append(key).append("\":"); - } -} diff --git a/src/com/wenshuo/agent/log/OutputLogRunnable.java b/src/com/wenshuo/agent/log/OutputLogRunnable.java deleted file mode 100644 index 3a08620..0000000 --- a/src/com/wenshuo/agent/log/OutputLogRunnable.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * @(#)OutputLogRunnable.java 2015-7-28 下午03:27:20 - * javaagent - * Copyright 2015 wenshuo, Inc. All rights reserved. - * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ -package com.wenshuo.agent.log; - -/** - * OutputLogRunnable - * - * @author dingjsh - * @time 2015-7-28下午03:27:20 - */ -public class OutputLogRunnable implements Runnable { - - /* - * (non-Javadoc) - * - * @see java.lang.Runnable#run() - */ - @Override - public void run() { - try{ - ExecuteLogUtils.outputCounterLog(); - }catch(Exception e){ - System.err.println(e); - } - } - -} diff --git a/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java b/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java deleted file mode 100644 index 6343783..0000000 --- a/src/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.wenshuo.agent.transformer; - -import com.wenshuo.agent.ConfigUtils; -import com.wenshuo.agent.PojoDetector; -import com.wenshuo.agent.javassist.CannotCompileException; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.LoaderClassPath; -import com.wenshuo.agent.javassist.Modifier; -import com.wenshuo.agent.javassist.NotFoundException; -import java.io.IOException; -import java.lang.instrument.ClassFileTransformer; -import java.security.ProtectionDomain; -import java.util.Collections; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * AgentLogClassFileTransformer 类增强,增加agent日志 - * - * @author dingjsh - * @time 2015-7-24上午09:53:44 - */ -public class AgentLogClassFileTransformer implements ClassFileTransformer { - - private static final String LOG_UTILS = "com.wenshuo.agent.log.ExecuteLogUtils"; - - private static final String AGENT_PACKAGE_NAME = "com.wenshuo.agent"; - - - @Override - public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, - ProtectionDomain protectionDomain, byte[] classfileBuffer) { - byte[] byteCode = classfileBuffer; - className = className.replace('/', '.'); - if (!isNeedLogExecuteInfo(className)) { - return byteCode; - } - if (null == loader) { - loader = Thread.currentThread().getContextClassLoader(); - } - byteCode = aopLog(loader, className, byteCode); - return byteCode; - } - - private byte[] aopLog(ClassLoader loader, String className, byte[] byteCode) { - try { - ClassPool cp = ClassPool.getDefault(); - CtClass cc; - try { - cc = cp.get(className); - } catch (NotFoundException e) { - cp.insertClassPath(new LoaderClassPath(loader)); - cc = cp.get(className); - } - byteCode = aopLog(cc, className, byteCode); - } catch (Exception ex) { - System.err.println(ex); - } - return byteCode; - } - - private byte[] aopLog(CtClass cc, String className, byte[] byteCode) throws CannotCompileException, IOException { - if (null == cc) { - return byteCode; - } - if (!cc.isInterface()) { - CtMethod[] methods = cc.getDeclaredMethods(); - if (null != methods && methods.length > 0) { - boolean isOpenPojoMonitor = ConfigUtils.isOpenPojoMonitor(); - Set getSetMethods = Collections.emptySet(); - if (!isOpenPojoMonitor) { - getSetMethods = PojoDetector.getPojoMethodNames(methods); - } - for (CtMethod m : methods) { - if (isOpenPojoMonitor || !getSetMethods.contains(m.getName())) { - aopLog(className, m); - } - } - byteCode = cc.toBytecode(); - } - } - cc.detach(); - return byteCode; - } - - private void aopLog(String className, CtMethod m) throws CannotCompileException { - if (null == m || m.isEmpty()) { - return; - } - boolean isMethodStatic = Modifier.isStatic(m.getModifiers()); - String aopClassName = isMethodStatic ? "\"" + className + "\"" : "this.getClass().getName()"; - final String timeMethodStr - = ConfigUtils.isUsingNanoTime() ? "java.lang.System.nanoTime()" : "java.lang.System.currentTimeMillis()"; - - // 避免变量名重复 - m.addLocalVariable("dingjsh_javaagent_elapsedTime", CtClass.longType); - m.insertBefore("dingjsh_javaagent_elapsedTime = " + timeMethodStr + ";"); - m.insertAfter("dingjsh_javaagent_elapsedTime = " + timeMethodStr + " - dingjsh_javaagent_elapsedTime;"); - m.insertAfter(LOG_UTILS + ".log(" + aopClassName + ",\"" + m.getName() - + "\",(long)dingjsh_javaagent_elapsedTime" + ");"); - } - - /** - * 是否需要记录执行信息 - * - * @param className 类名 - * @return 是否需要记录执行信息 - * @author dingjsh - * @time 2015-7-27下午06:11:02 - */ - private boolean isNeedLogExecuteInfo(String className) { - // do not transform the agent class,prevent deadlock - if (className.startsWith(AGENT_PACKAGE_NAME)) { - return false; - } - Set includes = ConfigUtils.getIncludePackages(); - if (null == includes || includes.isEmpty()) { - return false; - } - - boolean isNeeded = false; - // include package - for (String packageName : includes) { - if (className.startsWith(packageName)) { - isNeeded = true; - break; - } - } - // exclude package - if (isNeeded) { - Set excludes = ConfigUtils.getExcludePackages(); - if (null != excludes && !excludes.isEmpty()) { - for (String packageName : excludes) { - if (className.startsWith(packageName)) { - isNeeded = false; - break; - } - } - } - } - if (isNeeded) { - Set excludeClassRegexs = ConfigUtils.getExcludeClassRegexs(); - if (null != excludeClassRegexs && !excludeClassRegexs.isEmpty()) { - for (String regex : excludeClassRegexs) { - isNeeded = !Pattern.matches(regex, className); - if (!isNeeded) { - break; - } - } - } - } - return isNeeded; - } -} diff --git a/src/props/agent.properties b/src/props/agent.properties deleted file mode 100644 index 7d62605..0000000 --- a/src/props/agent.properties +++ /dev/null @@ -1,16 +0,0 @@ -#\u9700\u8981\u76d1\u63a7\u7684\u5305\uff0c\u591a\u4e2a\u5305\u7528\u5206\u53f7\u5206\u9694 -agent.include.package=com.XXX -#\u4e0d\u9700\u8981\u76d1\u63a7\u7684\u5305\uff0c\u591a\u4e2a\u5305\u7528\u5206\u53f7\u5206\u9694\u3002 \u9700\u8981\u76d1\u63a7\u7684\u5305-\u4e0d\u9700\u8981\u76d1\u63a7\u7684\u5305\u5c31\u662f\u771f\u6b63\u8981\u76d1\u63a7\u7684\u5305 -agent.exclude.package= -#\u65e5\u5fd7\u6587\u4ef6\uff0c\u4f1a\u81ea\u52a8\u589e\u52a0\u65e5\u671f\u540e\u7f00 -agent.log.file=d\:\\agent.log -#\u65e5\u5fd7\u8f93\u51fa\u5468\u671f -agent.log.interval.seconds=600 -#\u4e0d\u9700\u8981\u76d1\u63a7\u7684\u7c7b\u7684\u6b63\u5219\u8868\u8fbe\u5f0f -agent.exclude.class.regex= -#\u662f\u5426\u8bb0\u5f55\u5e73\u5747\u65f6\u95f4 -agent.log.avg.execute.time=false -#\u9ed8\u8ba4\u4e0d\u9700\u8981\u76d1\u63a7\u7684\u7c7b\u7684\u6b63\u5219\u8868\u8fbe\u5f0f -agent.exclude.class.regex.default=.*EnhancerByCGLIB.*;.*FastClassByCGLIB.* -#\u8bb0\u5f55\u65b9\u6cd5\u7684\u8017\u65f6\u65f6\u662f\u91c7\u7528nanoTime,\u8fd8\u662fcurrentTimeMillis,nanoTime\u66f4\u51c6\u786e\uff0c\u4f46\u662f\u4f1a\u8017\u65f6\u4e00\u4e9b -agent.log.nano=true diff --git a/test/com/wenshuo/agent/test/TestCtMethod.java b/test/com/wenshuo/agent/test/TestCtMethod.java deleted file mode 100644 index ecca591..0000000 --- a/test/com/wenshuo/agent/test/TestCtMethod.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.wenshuo.agent.test; - -import java.util.Arrays; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.Modifier; -import com.wenshuo.agent.javassist.NotFoundException; - -public class TestCtMethod { - - private static List staticMethodNames = Arrays.asList("privateStatic","protectedStatic","publicStatic","defaultStatic"); - - @Test - public void staticMethodTest() throws NotFoundException{ - ClassPool cp = ClassPool.getDefault(); - CtClass ctClass = cp.get("com.wenshuo.agent.test.TestCtMethod"); - CtMethod[] ctMethods = ctClass.getDeclaredMethods(); - for(CtMethod ctMethod : ctMethods){ - String methodName = ctMethod.getName(); - if(staticMethodNames.contains(methodName)){ - Assert.assertTrue(methodName+" is static method", Modifier.isStatic(ctMethod.getModifiers())); - } - } - System.out.println("staticMethodTest is successful"); - } - - private static void privateStatic(){ - System.out.println("I'm private and static"); - } - - protected static void protectedStatic(){ - System.out.println("I'm private and static"); - } - - public static void publicStatic(){ - System.out.println("I'm public and static"); - } - static void defaultStatic(){ - System.out.println("I'm default and static"); - } - - - -} From 30b3680be799a3ba7d3e4c10b8ca3251813a447c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=BB=BA=E6=B0=B4?= Date: Mon, 16 Oct 2023 10:48:22 +0800 Subject: [PATCH 16/25] =?UTF-8?q?=E4=BF=AE=E6=94=B9javaagent=E5=8C=85?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=B8=BAjava=E9=A1=B9=E7=9B=AE=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=8C=85=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/wenshuo/agent/Agent.java | 27 + .../java/com/wenshuo/agent/AgentUtils.java | 180 ++ .../java/com/wenshuo/agent/ConfigConsts.java | 39 + .../java/com/wenshuo/agent/ConfigUtils.java | 161 ++ .../com/wenshuo/agent/NamedThreadFactory.java | 35 + .../java/com/wenshuo/agent/PojoDetector.java | 65 + .../agent/javassist/ByteArrayClassPath.java | 99 + .../javassist/CannotCompileException.java | 120 + .../agent/javassist/ClassClassPath.java | 97 + .../com/wenshuo/agent/javassist/ClassMap.java | 173 ++ .../wenshuo/agent/javassist/ClassPath.java | 68 + .../wenshuo/agent/javassist/ClassPool.java | 1233 ++++++++++ .../agent/javassist/ClassPoolTail.java | 430 ++++ .../agent/javassist/CodeConverter.java | 809 +++++++ .../com/wenshuo/agent/javassist/CtArray.java | 113 + .../wenshuo/agent/javassist/CtBehavior.java | 1219 ++++++++++ .../com/wenshuo/agent/javassist/CtClass.java | 1595 +++++++++++++ .../wenshuo/agent/javassist/CtClassType.java | 1761 +++++++++++++++ .../agent/javassist/CtConstructor.java | 403 ++++ .../com/wenshuo/agent/javassist/CtField.java | 1406 ++++++++++++ .../com/wenshuo/agent/javassist/CtMember.java | 319 +++ .../com/wenshuo/agent/javassist/CtMethod.java | 438 ++++ .../wenshuo/agent/javassist/CtNewClass.java | 126 ++ .../agent/javassist/CtNewConstructor.java | 319 +++ .../wenshuo/agent/javassist/CtNewMethod.java | 478 ++++ .../agent/javassist/CtNewNestedClass.java | 67 + .../javassist/CtNewWrappedConstructor.java | 103 + .../agent/javassist/CtNewWrappedMethod.java | 199 ++ .../agent/javassist/CtPrimitiveType.java | 112 + .../com/wenshuo/agent/javassist/Loader.java | 430 ++++ .../agent/javassist/LoaderClassPath.java | 96 + .../com/wenshuo/agent/javassist/Modifier.java | 219 ++ .../agent/javassist/NotFoundException.java | 30 + .../agent/javassist/SerialVersionUID.java | 213 ++ .../wenshuo/agent/javassist/Translator.java | 71 + .../wenshuo/agent/javassist/URLClassPath.java | 179 ++ .../agent/javassist/bytecode/AccessFlag.java | 134 ++ .../bytecode/AnnotationDefaultAttribute.java | 160 ++ .../bytecode/AnnotationsAttribute.java | 732 ++++++ .../javassist/bytecode/AttributeInfo.java | 303 +++ .../agent/javassist/bytecode/BadBytecode.java | 40 + .../bytecode/BootstrapMethodsAttribute.java | 123 + .../agent/javassist/bytecode/ByteArray.java | 77 + .../agent/javassist/bytecode/ByteStream.java | 194 ++ .../agent/javassist/bytecode/Bytecode.java | 1459 ++++++++++++ .../agent/javassist/bytecode/ClassFile.java | 932 ++++++++ .../javassist/bytecode/ClassFilePrinter.java | 153 ++ .../javassist/bytecode/ClassFileWriter.java | 791 +++++++ .../javassist/bytecode/CodeAnalyzer.java | 267 +++ .../javassist/bytecode/CodeAttribute.java | 595 +++++ .../javassist/bytecode/CodeIterator.java | 1604 +++++++++++++ .../agent/javassist/bytecode/ConstPool.java | 1995 +++++++++++++++++ .../javassist/bytecode/ConstantAttribute.java | 73 + .../bytecode/DeprecatedAttribute.java | 56 + .../agent/javassist/bytecode/Descriptor.java | 872 +++++++ .../bytecode/DuplicateMemberException.java | 31 + .../bytecode/EnclosingMethodAttribute.java | 142 ++ .../javassist/bytecode/ExceptionTable.java | 281 +++ .../bytecode/ExceptionsAttribute.java | 174 ++ .../agent/javassist/bytecode/FieldInfo.java | 276 +++ .../bytecode/InnerClassesAttribute.java | 242 ++ .../bytecode/InstructionPrinter.java | 295 +++ .../bytecode/LineNumberAttribute.java | 182 ++ .../bytecode/LocalVariableAttribute.java | 334 +++ .../bytecode/LocalVariableTypeAttribute.java | 63 + .../agent/javassist/bytecode/LongVector.java | 64 + .../agent/javassist/bytecode/MethodInfo.java | 566 +++++ .../bytecode/MethodParametersAttribute.java | 87 + .../agent/javassist/bytecode/Mnemonic.java | 239 ++ .../agent/javassist/bytecode/Opcode.java | 449 ++++ .../ParameterAnnotationsAttribute.java | 215 ++ .../bytecode/SignatureAttribute.java | 1160 ++++++++++ .../bytecode/SourceFileAttribute.java | 71 + .../agent/javassist/bytecode/StackMap.java | 576 +++++ .../javassist/bytecode/StackMapTable.java | 1049 +++++++++ .../bytecode/SyntheticAttribute.java | 56 + .../bytecode/TypeAnnotationsAttribute.java | 360 +++ .../javassist/bytecode/analysis/Analyzer.java | 423 ++++ .../bytecode/analysis/ControlFlow.java | 504 +++++ .../javassist/bytecode/analysis/Executor.java | 1047 +++++++++ .../javassist/bytecode/analysis/Frame.java | 289 +++ .../bytecode/analysis/FramePrinter.java | 148 ++ .../javassist/bytecode/analysis/IntQueue.java | 57 + .../bytecode/analysis/MultiArrayType.java | 130 ++ .../bytecode/analysis/MultiType.java | 314 +++ .../bytecode/analysis/Subroutine.java | 67 + .../bytecode/analysis/SubroutineScanner.java | 157 ++ .../javassist/bytecode/analysis/Type.java | 593 +++++ .../javassist/bytecode/analysis/Util.java | 48 + .../javassist/bytecode/analysis/package.html | 20 + .../bytecode/annotation/Annotation.java | 348 +++ .../bytecode/annotation/AnnotationImpl.java | 305 +++ .../annotation/AnnotationMemberValue.java | 96 + .../annotation/AnnotationsWriter.java | 354 +++ .../bytecode/annotation/ArrayMemberValue.java | 145 ++ .../annotation/BooleanMemberValue.java | 103 + .../bytecode/annotation/ByteMemberValue.java | 103 + .../bytecode/annotation/CharMemberValue.java | 104 + .../bytecode/annotation/ClassMemberValue.java | 140 ++ .../annotation/DoubleMemberValue.java | 105 + .../bytecode/annotation/EnumMemberValue.java | 126 ++ .../bytecode/annotation/FloatMemberValue.java | 105 + .../annotation/IntegerMemberValue.java | 110 + .../bytecode/annotation/LongMemberValue.java | 104 + .../bytecode/annotation/MemberValue.java | 89 + .../annotation/MemberValueVisitor.java | 39 + .../bytecode/annotation/NoSuchClassError.java | 40 + .../bytecode/annotation/ShortMemberValue.java | 104 + .../annotation/StringMemberValue.java | 104 + .../annotation/TypeAnnotationsWriter.java | 174 ++ .../bytecode/annotation/package.html | 8 + .../agent/javassist/bytecode/package.html | 18 + .../bytecode/stackmap/BasicBlock.java | 411 ++++ .../javassist/bytecode/stackmap/MapMaker.java | 606 +++++ .../javassist/bytecode/stackmap/Tracer.java | 933 ++++++++ .../javassist/bytecode/stackmap/TypeData.java | 785 +++++++ .../javassist/bytecode/stackmap/TypeTag.java | 30 + .../bytecode/stackmap/TypedBlock.java | 234 ++ .../javassist/compiler/AccessorMaker.java | 260 +++ .../agent/javassist/compiler/CodeGen.java | 1955 ++++++++++++++++ .../javassist/compiler/CompileError.java | 53 + .../agent/javassist/compiler/Javac.java | 610 +++++ .../agent/javassist/compiler/JvstCodeGen.java | 710 ++++++ .../javassist/compiler/JvstTypeChecker.java | 282 +++ .../javassist/compiler/KeywordTable.java | 33 + .../wenshuo/agent/javassist/compiler/Lex.java | 551 +++++ .../javassist/compiler/MemberCodeGen.java | 1160 ++++++++++ .../javassist/compiler/MemberResolver.java | 617 +++++ .../javassist/compiler/NoFieldException.java | 40 + .../agent/javassist/compiler/Parser.java | 1345 +++++++++++ .../javassist/compiler/ProceedHandler.java | 30 + .../agent/javassist/compiler/SymbolTable.java | 45 + .../agent/javassist/compiler/SyntaxError.java | 23 + .../agent/javassist/compiler/TokenId.java | 125 ++ .../agent/javassist/compiler/TypeChecker.java | 1044 +++++++++ .../agent/javassist/compiler/ast/ASTList.java | 160 ++ .../agent/javassist/compiler/ast/ASTree.java | 59 + .../javassist/compiler/ast/ArrayInit.java | 32 + .../javassist/compiler/ast/AssignExpr.java | 41 + .../agent/javassist/compiler/ast/BinExpr.java | 42 + .../javassist/compiler/ast/CallExpr.java | 47 + .../javassist/compiler/ast/CastExpr.java | 56 + .../javassist/compiler/ast/CondExpr.java | 44 + .../javassist/compiler/ast/Declarator.java | 128 ++ .../javassist/compiler/ast/DoubleConst.java | 95 + .../agent/javassist/compiler/ast/Expr.java | 85 + .../javassist/compiler/ast/FieldDecl.java | 35 + .../compiler/ast/InstanceOfExpr.java | 40 + .../javassist/compiler/ast/IntConst.java | 139 ++ .../agent/javassist/compiler/ast/Keyword.java | 36 + .../agent/javassist/compiler/ast/Member.java | 40 + .../javassist/compiler/ast/MethodDecl.java | 46 + .../agent/javassist/compiler/ast/NewExpr.java | 78 + .../agent/javassist/compiler/ast/Pair.java | 52 + .../agent/javassist/compiler/ast/Stmnt.java | 60 + .../agent/javassist/compiler/ast/StringL.java | 36 + .../agent/javassist/compiler/ast/Symbol.java | 36 + .../javassist/compiler/ast/Variable.java | 39 + .../agent/javassist/compiler/ast/Visitor.java | 52 + .../convert/TransformAccessArrayField.java | 270 +++ .../javassist/convert/TransformAfter.java | 47 + .../javassist/convert/TransformBefore.java | 108 + .../javassist/convert/TransformCall.java | 130 ++ .../convert/TransformFieldAccess.java | 82 + .../agent/javassist/convert/TransformNew.java | 103 + .../javassist/convert/TransformNewClass.java | 83 + .../javassist/convert/TransformReadField.java | 96 + .../convert/TransformWriteField.java | 72 + .../agent/javassist/convert/Transformer.java | 57 + .../wenshuo/agent/javassist/expr/Cast.java | 166 ++ .../agent/javassist/expr/ConstructorCall.java | 70 + .../wenshuo/agent/javassist/expr/Expr.java | 330 +++ .../agent/javassist/expr/ExprEditor.java | 317 +++ .../agent/javassist/expr/FieldAccess.java | 325 +++ .../wenshuo/agent/javassist/expr/Handler.java | 146 ++ .../agent/javassist/expr/Instanceof.java | 170 ++ .../agent/javassist/expr/MethodCall.java | 247 ++ .../agent/javassist/expr/NewArray.java | 283 +++ .../wenshuo/agent/javassist/expr/NewExpr.java | 249 ++ .../wenshuo/agent/javassist/expr/package.html | 8 + .../com/wenshuo/agent/javassist/package.html | 22 + .../agent/javassist/runtime/Cflow.java | 53 + .../wenshuo/agent/javassist/runtime/Desc.java | 161 ++ .../agent/javassist/runtime/DotClass.java | 29 + .../agent/javassist/runtime/Inner.java | 25 + .../agent/javassist/runtime/package.html | 12 + .../javassist/scopedpool/ScopedClassPool.java | 309 +++ .../scopedpool/ScopedClassPoolFactory.java | 39 + .../ScopedClassPoolFactoryImpl.java | 43 + .../scopedpool/ScopedClassPoolRepository.java | 98 + .../ScopedClassPoolRepositoryImpl.java | 188 ++ .../scopedpool/SoftValueHashMap.java | 233 ++ .../agent/javassist/scopedpool/package.html | 7 + .../agent/javassist/tools/Callback.java | 152 ++ .../wenshuo/agent/javassist/tools/Dump.java | 58 + .../agent/javassist/tools/framedump.java | 48 + .../agent/javassist/tools/package.html | 6 + .../tools/reflect/CannotCreateException.java | 30 + .../tools/reflect/CannotInvokeException.java | 69 + .../tools/reflect/CannotReflectException.java | 35 + .../tools/reflect/ClassMetaobject.java | 370 +++ .../javassist/tools/reflect/Compiler.java | 163 ++ .../agent/javassist/tools/reflect/Loader.java | 162 ++ .../javassist/tools/reflect/Metalevel.java | 39 + .../javassist/tools/reflect/Metaobject.java | 239 ++ .../javassist/tools/reflect/Reflection.java | 403 ++++ .../agent/javassist/tools/reflect/Sample.java | 57 + .../javassist/tools/reflect/package.html | 35 + .../javassist/tools/rmi/AppletServer.java | 251 +++ .../javassist/tools/rmi/ObjectImporter.java | 299 +++ .../tools/rmi/ObjectNotFoundException.java | 27 + .../agent/javassist/tools/rmi/Proxy.java | 26 + .../javassist/tools/rmi/RemoteException.java | 31 + .../agent/javassist/tools/rmi/RemoteRef.java | 36 + .../agent/javassist/tools/rmi/Sample.java | 37 + .../javassist/tools/rmi/StubGenerator.java | 256 +++ .../agent/javassist/tools/rmi/package.html | 16 + .../javassist/tools/web/BadHttpRequest.java | 35 + .../agent/javassist/tools/web/Viewer.java | 209 ++ .../agent/javassist/tools/web/Webserver.java | 408 ++++ .../agent/javassist/tools/web/package.html | 7 + .../agent/javassist/util/HotSwapper.java | 251 +++ .../wenshuo/agent/javassist/util/package.html | 5 + .../javassist/util/proxy/FactoryHelper.java | 237 ++ .../javassist/util/proxy/MethodFilter.java | 31 + .../javassist/util/proxy/MethodHandler.java | 49 + .../agent/javassist/util/proxy/Proxy.java | 33 + .../javassist/util/proxy/ProxyFactory.java | 1446 ++++++++++++ .../javassist/util/proxy/ProxyObject.java | 44 + .../util/proxy/ProxyObjectInputStream.java | 100 + .../util/proxy/ProxyObjectOutputStream.java | 72 + .../javassist/util/proxy/RuntimeSupport.java | 271 +++ .../javassist/util/proxy/SecurityActions.java | 136 ++ .../javassist/util/proxy/SerializedProxy.java | 99 + .../agent/javassist/util/proxy/package.html | 6 + .../wenshuo/agent/log/ExecuteLogUtils.java | 316 +++ .../agent/log/MethodExecuteJSONformatter.java | 70 + .../wenshuo/agent/log/OutputLogRunnable.java | 31 + .../AgentLogClassFileTransformer.java | 155 ++ src/main/resources/agent.properties | 16 + .../com/wenshuo/agent/test/TestCtMethod.java | 50 + 241 files changed, 62385 insertions(+) create mode 100644 src/main/java/com/wenshuo/agent/Agent.java create mode 100644 src/main/java/com/wenshuo/agent/AgentUtils.java create mode 100644 src/main/java/com/wenshuo/agent/ConfigConsts.java create mode 100644 src/main/java/com/wenshuo/agent/ConfigUtils.java create mode 100644 src/main/java/com/wenshuo/agent/NamedThreadFactory.java create mode 100644 src/main/java/com/wenshuo/agent/PojoDetector.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/ByteArrayClassPath.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CannotCompileException.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/ClassClassPath.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/ClassMap.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/ClassPath.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/ClassPool.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/ClassPoolTail.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CodeConverter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtArray.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtBehavior.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtClass.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtClassType.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtConstructor.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtField.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtMember.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtMethod.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtNewClass.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtNewConstructor.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtNewMethod.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtNewNestedClass.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtNewWrappedMethod.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/CtPrimitiveType.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/Loader.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/LoaderClassPath.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/Modifier.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/NotFoundException.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/SerialVersionUID.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/Translator.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/URLClassPath.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/AccessFlag.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/BadBytecode.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ByteArray.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ByteStream.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/Bytecode.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFile.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/CodeIterator.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ConstPool.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/Descriptor.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/FieldInfo.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/LongVector.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/MethodInfo.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/Mnemonic.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/Opcode.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/StackMap.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/StackMapTable.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Type.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Util.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/AccessorMaker.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/CodeGen.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/CompileError.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/Javac.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/KeywordTable.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/Lex.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/MemberResolver.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/NoFieldException.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/Parser.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ProceedHandler.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/SymbolTable.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/SyntaxError.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/TokenId.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/TypeChecker.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTList.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTree.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/Declarator.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/Expr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/IntConst.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/Keyword.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/Member.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/Pair.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/StringL.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/Symbol.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/Variable.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/compiler/ast/Visitor.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/TransformAfter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/TransformBefore.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/TransformCall.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/TransformNew.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/TransformNewClass.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/TransformReadField.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/TransformWriteField.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/convert/Transformer.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/Cast.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/ConstructorCall.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/Expr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/ExprEditor.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/FieldAccess.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/Handler.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/Instanceof.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/MethodCall.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/NewArray.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/NewExpr.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/expr/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/runtime/Cflow.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/runtime/Desc.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/runtime/DotClass.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/runtime/Inner.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/runtime/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/scopedpool/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/Callback.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/Dump.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/framedump.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/Compiler.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/Loader.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/Reflection.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/Sample.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/reflect/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/rmi/Proxy.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/rmi/Sample.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/rmi/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/web/Viewer.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/web/Webserver.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/tools/web/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/HotSwapper.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/package.html create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/Proxy.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java create mode 100644 src/main/java/com/wenshuo/agent/javassist/util/proxy/package.html create mode 100644 src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java create mode 100644 src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java create mode 100644 src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java create mode 100644 src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java create mode 100644 src/main/resources/agent.properties create mode 100644 src/test/java/com/wenshuo/agent/test/TestCtMethod.java diff --git a/src/main/java/com/wenshuo/agent/Agent.java b/src/main/java/com/wenshuo/agent/Agent.java new file mode 100644 index 0000000..c32f651 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/Agent.java @@ -0,0 +1,27 @@ +/* + * @(#)Agent.java 2015-7-24 上午09:49:34 + * javaagent + * Copyright 2015 wenshuo, Inc. All rights reserved. + * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ +package com.wenshuo.agent; +import java.lang.instrument.Instrumentation; +import com.wenshuo.agent.log.ExecuteLogUtils; +import com.wenshuo.agent.transformer.AgentLogClassFileTransformer; + +/** + * Agent + * + * @author dingjsh + */ +public class Agent { + + public static void premain(String agentArs, Instrumentation inst) { + System.out.println("javaagent启动成功,将自动记录方法的执行次数和时间"); + // 初始化配置 + ConfigUtils.initProperties(agentArs); + ExecuteLogUtils.init(); + inst.addTransformer(new AgentLogClassFileTransformer()); + } + +} diff --git a/src/main/java/com/wenshuo/agent/AgentUtils.java b/src/main/java/com/wenshuo/agent/AgentUtils.java new file mode 100644 index 0000000..e133898 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/AgentUtils.java @@ -0,0 +1,180 @@ +/* + * @(#)AgentUtils.java 2015-7-27 下午05:26:24 + * javaagent + * Copyright 2015 wenshuo, Inc. All rights reserved. + * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ +package com.wenshuo.agent; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; + +/** + * AgentUtils + * + * @author dingjsh + * @time 2015-7-27下午05:26:24 + */ +public class AgentUtils { + + /** + *

+ * Checks if a String is whitespace, empty ("") or null. + *

+ * + *
+     * StringUtils.isBlank(null)      = true
+     * StringUtils.isBlank("")        = true
+     * StringUtils.isBlank(" ")       = true
+     * StringUtils.isBlank("bob")     = false
+     * StringUtils.isBlank("  bob  ") = false
+     * 
+ * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * @since 2.0 + */ + public static boolean isBlank(String str) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + *

+ * Checks if a String is not empty (""), not null and not whitespace only. + *

+ * + *
+     * StringUtils.isNotBlank(null)      = false
+     * StringUtils.isNotBlank("")        = false
+     * StringUtils.isNotBlank(" ")       = false
+     * StringUtils.isNotBlank("bob")     = true
+     * StringUtils.isNotBlank("  bob  ") = true
+     * 
+ * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not + * whitespace + * @since 2.0 + */ + static boolean isNotBlank(String str) { + return !isBlank(str); + } + + /** + * Unconditionally close an InputStream. + *

+ * Equivalent to {@link InputStream#close()}, except any exceptions will be + * ignored. This is typically used in finally blocks. + * + * @param input the InputStream to close, may be null or already closed + */ + static void closeQuietly(InputStream input) { + try { + if (input != null) { + input.close(); + } + } catch (IOException ioe) { + // ignore + } + } + + public static int copy(InputStream input, OutputStream output) + throws IOException { + long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; + } + + private static long copyLarge(InputStream input, OutputStream output) + throws IOException { + byte[] buffer = new byte[4096]; + long count = 0; + int n; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + // read toByteArray + //----------------------------------------------------------------------- + + /** + * Get the contents of an InputStream as a byte[]. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(InputStream input) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy(input, output); + return output.toByteArray(); + } + + /** + * Unconditionally close a Writer. + *

+ * Equivalent to {@link Writer#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + * + * @param output the Writer to close, may be null or already closed + */ + public static void closeQuietly(Writer output) { + try { + if (output != null) { + output.close(); + } + } catch (IOException ioe) { + // ignore + } + } + + /** + * Makes a directory, including any necessary but nonexistent parent + * directories. If there already exists a file with specified name or + * the directory cannot be created then an exception is thrown. + * + * @param directory directory to create, must not be null + * @throws NullPointerException if the directory is null + * @throws IOException if the directory cannot be created + */ + public static void forceMkdir(File directory) throws IOException { + if (directory.exists()) { + if (directory.isFile()) { + String message = + "File " + + directory + + " exists and is " + + "not a directory. Unable to create directory."; + throw new IOException(message); + } + } else { + if (!directory.mkdirs()) { + String message = + "Unable to create directory " + directory; + throw new IOException(message); + } + } + } +} diff --git a/src/main/java/com/wenshuo/agent/ConfigConsts.java b/src/main/java/com/wenshuo/agent/ConfigConsts.java new file mode 100644 index 0000000..6edd2c4 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/ConfigConsts.java @@ -0,0 +1,39 @@ +/* + * @(#)ConfigConsts.java 2015-7-27 下午06:06:23 + * javaagent + * Copyright 2015 wenshuo, Inc. All rights reserved. + * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ +package com.wenshuo.agent; + +/** + * ConfigConsts + * + * @author dingjsh + * @time 2015-7-27下午06:06:23 + */ +public interface ConfigConsts { + + String EXCLUDE_PACKAGE_DEFAULT = "agent.exclude.package.default"; + + String EXCLUDE_PACKAGE = "agent.exclude.package"; + + String INCLUDE_PACKAGE = "agent.include.package"; + + String LOG_FILE = "agent.log.file"; + + String LOG_INTERVAL_SECONDS = "agent.log.interval.seconds"; + + String EXCLUDE_CLASS_REGEX = "agent.exclude.class.regex"; + + String EXCLUDE_CLASS_REGEX_DEFAULT = "agent.exclude.class.regex.default"; + + String LOG_AVG_EXECUTE_TIME = "agent.log.avg.execute.time"; + + String POJO_MONITOR_OPEN = "agent.pojo.monitor.open"; + + String LOG_TIME_NANO = "agent.log.nano"; + + int DEFAULT_LOG_INTERVAL = 600; + +} diff --git a/src/main/java/com/wenshuo/agent/ConfigUtils.java b/src/main/java/com/wenshuo/agent/ConfigUtils.java new file mode 100644 index 0000000..400a9be --- /dev/null +++ b/src/main/java/com/wenshuo/agent/ConfigUtils.java @@ -0,0 +1,161 @@ +package com.wenshuo.agent; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +/** + * 获取配置信息
+ * ConfigUtils + * + * @author dingjsh + * @time 2015-7-27下午09:16:18 + */ +public class ConfigUtils { + + private static Properties props; + + private static Set excludePackages; + + private static Set includePackages; + + private static Set excludeClassRegexs; + + private ConfigUtils() { + super(); + } + + static void initProperties(String propertiesFileName) { + props = getProperties(propertiesFileName); + initExcludePackages(); + initIncludePackages(); + initExcludeClassRegexs(); + } + + private static String getProperty(String key) { + if (null != props && AgentUtils.isNotBlank(key)) { + return props.getProperty(key); + } + return null; + } + + private static Properties getProperties(String propertiesFileName) { + Properties properties = new Properties(); + InputStream input = null; + try { + input = ConfigUtils.class.getClassLoader().getResourceAsStream("agent.properties"); + properties.load(input); + } catch (Exception e) { + System.err.println("未找到默认配置"); + } finally { + AgentUtils.closeQuietly(input); + } + if (AgentUtils.isNotBlank(propertiesFileName)) { + try { + input = new FileInputStream(propertiesFileName); + properties.load(input); + } catch (Exception e) { + System.err.println(e); + } finally { + AgentUtils.closeQuietly(input); + } + } + return properties; + } + + public static Set getExcludePackages() { + return excludePackages; + } + + private static void initExcludePackages() { + if (null == excludePackages) { + String excludeDefault = getProperty(ConfigConsts.EXCLUDE_PACKAGE_DEFAULT); + String excludes = getProperty(ConfigConsts.EXCLUDE_PACKAGE); + excludePackages = new HashSet(); + if (AgentUtils.isNotBlank(excludeDefault)) { + excludePackages.addAll(Arrays.asList(excludeDefault.split(";"))); + } + if (AgentUtils.isNotBlank(excludes)) { + excludePackages.addAll(Arrays.asList(excludes.split(";"))); + } + } + } + + public static Set getIncludePackages() { + return includePackages; + } + + private static void initIncludePackages() { + if (null == includePackages) { + String includes = getProperty(ConfigConsts.INCLUDE_PACKAGE); + includePackages = new HashSet(); + if (AgentUtils.isNotBlank(includes)) { + includePackages.addAll(Arrays.asList(includes.split(";"))); + } + } + } + + public static String getLogFileName() { + return getProperty(ConfigConsts.LOG_FILE); + } + + public static int getLogInterval() { + String intervalStr = getProperty(ConfigConsts.LOG_INTERVAL_SECONDS); + if (AgentUtils.isBlank(intervalStr)) { + return ConfigConsts.DEFAULT_LOG_INTERVAL; + } + return Integer.parseInt(intervalStr); + } + + public static Set getExcludeClassRegexs() { + return excludeClassRegexs; + } + + private static void initExcludeClassRegexs() { + if (null == excludeClassRegexs) { + Set excludeClassRegexsTemp = new HashSet(); + String defaultRegex = getProperty(ConfigConsts.EXCLUDE_CLASS_REGEX_DEFAULT); + if (AgentUtils.isNotBlank(defaultRegex)) { + excludeClassRegexsTemp.addAll(Arrays.asList(defaultRegex.split(";"))); + } + String excludeRegexStr = getProperty(ConfigConsts.EXCLUDE_CLASS_REGEX); + if (AgentUtils.isNotBlank(excludeRegexStr)) { + excludeClassRegexsTemp.addAll(Arrays.asList(excludeRegexStr.split(";"))); + } + excludeClassRegexs = excludeClassRegexsTemp; + } + } + + public static boolean isLogAvgExecuteTime() { + String value = getProperty(ConfigConsts.LOG_AVG_EXECUTE_TIME); + return "true".equalsIgnoreCase(value); + } + + /** + * 是否开启pojo的监控 + * + * @return 是否开启pojo的监控 + * @author dingjsh + */ + public static boolean isOpenPojoMonitor() { + String value = getProperty(ConfigConsts.POJO_MONITOR_OPEN); + return "true".equalsIgnoreCase(value); + } + + /** + * ConfigUtils + * + * @return 是否采用nanoTime来记录方法的执行时间 + * @description 是否采用nanoTime来记录方法的执行时间 + * @author dingjsh + * @date 2018年5月25日 下午2:08:33 + * @version 1.2.0 + */ + public static boolean isUsingNanoTime() { + String value = getProperty(ConfigConsts.LOG_TIME_NANO); + return "true".equalsIgnoreCase(value); + } +} diff --git a/src/main/java/com/wenshuo/agent/NamedThreadFactory.java b/src/main/java/com/wenshuo/agent/NamedThreadFactory.java new file mode 100644 index 0000000..6daceec --- /dev/null +++ b/src/main/java/com/wenshuo/agent/NamedThreadFactory.java @@ -0,0 +1,35 @@ +package com.wenshuo.agent; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 此类是拷贝的dubbo中的NamedThreadFactory,在创建线程池时总是没有名字,区分不出来是哪个线程池创建的,不便于调试 + * NamedThreadFactory + * @author dingjsh + * @time 2016-5-19上午09:40:50 + */ +public class NamedThreadFactory implements ThreadFactory { + private final AtomicInteger mThreadNum = new AtomicInteger(1); + + private final String mPrefix; + + private final boolean mDaemon; + + private final ThreadGroup mGroup; + + public NamedThreadFactory(String prefix, boolean daemon) { + mPrefix = prefix + "-thread-"; + mDaemon = daemon; + SecurityManager s = System.getSecurityManager(); + mGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s + .getThreadGroup(); + } + + public Thread newThread(Runnable runnable) { + String name = mPrefix + mThreadNum.getAndIncrement(); + Thread ret = new Thread(mGroup, runnable, name, 0); + ret.setDaemon(mDaemon); + return ret; + } +} \ No newline at end of file diff --git a/src/main/java/com/wenshuo/agent/PojoDetector.java b/src/main/java/com/wenshuo/agent/PojoDetector.java new file mode 100644 index 0000000..48cf45c --- /dev/null +++ b/src/main/java/com/wenshuo/agent/PojoDetector.java @@ -0,0 +1,65 @@ +/* + * @(#)PojoDetector.java 2016-5-7 下午04:18:14 + * javaagent + * Copyright 2016 wenshuo, Inc. All rights reserved. + * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ +package com.wenshuo.agent; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.wenshuo.agent.javassist.CtMethod; + +/** + * PojoDetector + * @author dingjsh + * @time 2016-5-7下午04:18:14 + */ +public class PojoDetector { + + public static Set getPojoMethodNames(CtMethod[] methods){ + Set pojoMethods = Collections.emptySet(); + if(null != methods && methods.length>0){ + pojoMethods = new HashSet(); + Map propName2MethodName = new HashMap(); + for(CtMethod method : methods){ + String methodName = method.getName(); + if(isSetMethod(methodName) || isGetMethod(methodName)){ + String propName = getPojoPropertyName(methodName); + if(propName2MethodName.containsKey(propName)){ + pojoMethods.add(methodName); + pojoMethods.add(propName2MethodName.get(propName)); + }else{ + propName2MethodName.put(propName, methodName); + } + } + } + } + return pojoMethods; + } + + + + private static boolean isGetMethod(String methodName){ + return methodName.startsWith("get") || methodName.startsWith("is"); + } + + private static boolean isSetMethod(String methodName){ + return methodName.startsWith("set"); + } + + private static String getPojoPropertyName(String methodName){ + if(methodName.startsWith("get") || methodName.startsWith("set")){ + return methodName.substring(3); + }else if(methodName.startsWith("is")){ + return methodName.substring(2); + } + return ""; + } + + +} diff --git a/src/main/java/com/wenshuo/agent/javassist/ByteArrayClassPath.java b/src/main/java/com/wenshuo/agent/javassist/ByteArrayClassPath.java new file mode 100644 index 0000000..d4d9347 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/ByteArrayClassPath.java @@ -0,0 +1,99 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.*; +import java.net.URL; +import java.net.MalformedURLException; + +/** + * A ByteArrayClassPath contains bytes that is served as + * a class file to a ClassPool. It is useful to convert + * a byte array to a CtClass object. + * + *

For example, if you want to convert a byte array b + * into a CtClass object representing the class with a name + * classname, then do as following: + * + *

+ * ClassPool cp = ClassPool.getDefault();
+ * cp.insertClassPath(new ByteArrayClassPath(classname, b));
+ * CtClass cc = cp.get(classname);
+ * 
+ * + *

The ClassPool object cp uses the created + * ByteArrayClassPath object as the source of the class file. + * + *

A ByteArrayClassPath must be instantiated for every + * class. It contains only a single class file. + * + * @see javassist.ClassPath + * @see ClassPool#insertClassPath(ClassPath) + * @see ClassPool#appendClassPath(ClassPath) + * @see ClassPool#makeClass(InputStream) + */ +public class ByteArrayClassPath implements ClassPath { + protected String classname; + protected byte[] classfile; + + /* + * Creates a ByteArrayClassPath containing the given + * bytes. + * + * @param name a fully qualified class name + * @param classfile the contents of a class file. + */ + public ByteArrayClassPath(String name, byte[] classfile) { + this.classname = name; + this.classfile = classfile; + } + + /** + * Closes this class path. + */ + public void close() {} + + public String toString() { + return "byte[]:" + classname; + } + + /** + * Opens the class file. + */ + public InputStream openClassfile(String classname) { + if(this.classname.equals(classname)) + return new ByteArrayInputStream(classfile); + else + return null; + } + + /** + * Obtains the URL. + */ + public URL find(String classname) { + if(this.classname.equals(classname)) { + String cname = classname.replace('.', '/') + ".class"; + try { + // return new File(cname).toURL(); + return new URL("file:/ByteArrayClassPath/" + cname); + } + catch (MalformedURLException e) {} + } + + return null; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CannotCompileException.java b/src/main/java/com/wenshuo/agent/javassist/CannotCompileException.java new file mode 100644 index 0000000..31057c8 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CannotCompileException.java @@ -0,0 +1,120 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Thrown when bytecode transformation has failed. + */ +public class CannotCompileException extends Exception { + private Throwable myCause; + + /** + * Gets the cause of this throwable. + * It is for JDK 1.3 compatibility. + */ + public Throwable getCause() { + return (myCause == this ? null : myCause); + } + + /** + * Initializes the cause of this throwable. + * It is for JDK 1.3 compatibility. + */ + public synchronized Throwable initCause(Throwable cause) { + myCause = cause; + return this; + } + + private String message; + + /** + * Gets a long message if it is available. + */ + public String getReason() { + if (message != null) + return message; + else + return this.toString(); + } + + /** + * Constructs a CannotCompileException with a message. + * + * @param msg the message. + */ + public CannotCompileException(String msg) { + super(msg); + message = msg; + initCause(null); + } + + /** + * Constructs a CannotCompileException with an Exception + * representing the cause. + * + * @param e the cause. + */ + public CannotCompileException(Throwable e) { + super("by " + e.toString()); + message = null; + initCause(e); + } + + /** + * Constructs a CannotCompileException with a detailed message + * and an Exception representing the cause. + * + * @param msg the message. + * @param e the cause. + */ + public CannotCompileException(String msg, Throwable e) { + this(msg); + initCause(e); + } + + /** + * Constructs a CannotCompileException with a + * NotFoundException. + */ + public CannotCompileException(NotFoundException e) { + this("cannot find " + e.getMessage(), e); + } + + /** + * Constructs a CannotCompileException with an CompileError. + */ + public CannotCompileException(CompileError e) { + this("[source error] " + e.getMessage(), e); + } + + /** + * Constructs a CannotCompileException + * with a ClassNotFoundException. + */ + public CannotCompileException(ClassNotFoundException e, String name) { + this("cannot find " + name, e); + } + + /** + * Constructs a CannotCompileException with a ClassFormatError. + */ + public CannotCompileException(ClassFormatError e, String name) { + this("invalid class format: " + name, e); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassClassPath.java b/src/main/java/com/wenshuo/agent/javassist/ClassClassPath.java new file mode 100644 index 0000000..be42a50 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/ClassClassPath.java @@ -0,0 +1,97 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.InputStream; +import java.net.URL; + +/** + * A search-path for obtaining a class file + * by getResourceAsStream() in java.lang.Class. + * + *

Try adding a ClassClassPath when a program is running + * with a user-defined class loader and any class files are not found with + * the default ClassPool. For example, + * + *

+ * ClassPool cp = ClassPool.getDefault();
+ * cp.insertClassPath(new ClassClassPath(this.getClass()));
+ * 
+ * + * This code snippet permanently adds a ClassClassPath + * to the default ClassPool. Note that the default + * ClassPool is a singleton. The added + * ClassClassPath uses a class object representing + * the class including the code snippet above. + * + * @see ClassPool#insertClassPath(ClassPath) + * @see ClassPool#appendClassPath(ClassPath) + * @see LoaderClassPath + */ +public class ClassClassPath implements ClassPath { + private Class thisClass; + + /** Creates a search path. + * + * @param c the Class object used to obtain a class + * file. getResourceAsStream() is called on + * this object. + */ + public ClassClassPath(Class c) { + thisClass = c; + } + + ClassClassPath() { + /* The value of thisClass was this.getClass() in early versions: + * + * thisClass = this.getClass(); + * + * However, this made openClassfile() not search all the system + * class paths if javassist.jar is put in jre/lib/ext/ + * (with JDK1.4). + */ + this(java.lang.Object.class); + } + + /** + * Obtains a class file by getResourceAsStream(). + */ + public InputStream openClassfile(String classname) { + String jarname = "/" + classname.replace('.', '/') + ".class"; + return thisClass.getResourceAsStream(jarname); + } + + /** + * Obtains the URL of the specified class file. + * + * @return null if the class file could not be found. + */ + public URL find(String classname) { + String jarname = "/" + classname.replace('.', '/') + ".class"; + return thisClass.getResource(jarname); + } + + /** + * Does nothing. + */ + public void close() { + } + + public String toString() { + return thisClass.getName() + ".class"; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassMap.java b/src/main/java/com/wenshuo/agent/javassist/ClassMap.java new file mode 100644 index 0000000..2dbf987 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/ClassMap.java @@ -0,0 +1,173 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.Descriptor; + +/** + * A hash table associating class names with different names. + * + *

This hashtable is used for replacing class names in a class + * definition or a method body. Define a subclass of this class + * if a more complex mapping algorithm is needed. For example, + * + *

class MyClassMap extends ClassMap {
+ *   public Object get(Object jvmClassName) {
+ *     String name = toJavaName((String)jvmClassName);
+ *     if (name.startsWith("java."))
+ *         return toJvmName("java2." + name.substring(5));
+ *     else
+ *         return super.get(jvmClassName);
+ *   }
+ * }
+ * + *

This subclass maps java.lang.String to + * java2.lang.String. Note that get() + * receives and returns the internal representation of a class name. + * For example, the internal representation of java.lang.String + * is java/lang/String. + * + *

Note that this is a map from String to String. + * + * @see #get(Object) + * @see CtClass#replaceClassName(ClassMap) + * @see CtNewMethod#copy(CtMethod,String,CtClass,ClassMap) + */ +public class ClassMap extends java.util.HashMap { + private ClassMap parent; + + /** + * Constructs a hash table. + */ + public ClassMap() { parent = null; } + + ClassMap(ClassMap map) { parent = map; } + + /** + * Maps a class name to another name in this hashtable. + * The names are obtained with calling Class.getName(). + * This method translates the given class names into the + * internal form used in the JVM before putting it in + * the hashtable. + * + * @param oldname the original class name + * @param newname the substituted class name. + */ + public void put(CtClass oldname, CtClass newname) { + put(oldname.getName(), newname.getName()); + } + + /** + * Maps a class name to another name in this hashtable. + * If the hashtable contains another mapping from the same + * class name, the old mapping is replaced. + * This method translates the given class names into the + * internal form used in the JVM before putting it in + * the hashtable. + * + *

If oldname is identical to + * newname, then this method does not + * perform anything; it does not record the mapping from + * oldname to newname. See + * fix method. + * + * @param oldname the original class name. + * @param newname the substituted class name. + * @see #fix(String) + */ + public void put(String oldname, String newname) { + if (oldname == newname) + return; + + String oldname2 = toJvmName(oldname); + String s = (String)get(oldname2); + if (s == null || !s.equals(oldname2)) + super.put(oldname2, toJvmName(newname)); + } + + /** + * Is equivalent to put() except that + * the given mapping is not recorded into the hashtable + * if another mapping from oldname is + * already included. + * + * @param oldname the original class name. + * @param newname the substituted class name. + */ + public void putIfNone(String oldname, String newname) { + if (oldname == newname) + return; + + String oldname2 = toJvmName(oldname); + String s = (String)get(oldname2); + if (s == null) + super.put(oldname2, toJvmName(newname)); + } + + protected final void put0(Object oldname, Object newname) { + super.put(oldname, newname); + } + + /** + * Returns the class name to wihch the given jvmClassName + * is mapped. A subclass of this class should override this method. + * + *

This method receives and returns the internal representation of + * class name used in the JVM. + * + * @see #toJvmName(String) + * @see #toJavaName(String) + */ + public Object get(Object jvmClassName) { + Object found = super.get(jvmClassName); + if (found == null && parent != null) + return parent.get(jvmClassName); + else + return found; + } + + /** + * Prevents a mapping from the specified class name to another name. + */ + public void fix(CtClass clazz) { + fix(clazz.getName()); + } + + /** + * Prevents a mapping from the specified class name to another name. + */ + public void fix(String name) { + String name2 = toJvmName(name); + super.put(name2, name2); + } + + /** + * Converts a class name into the internal representation used in + * the JVM. + */ + public static String toJvmName(String classname) { + return Descriptor.toJvmName(classname); + } + + /** + * Converts a class name from the internal representation used in + * the JVM to the normal one used in Java. + */ + public static String toJavaName(String classname) { + return Descriptor.toJavaName(classname); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassPath.java b/src/main/java/com/wenshuo/agent/javassist/ClassPath.java new file mode 100644 index 0000000..f0e90e2 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/ClassPath.java @@ -0,0 +1,68 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.InputStream; +import java.net.URL; + +/** + * ClassPath is an interface implemented by objects + * representing a class search path. + * ClassPool uses those objects for reading class files. + * + *

The users can define a class implementing this interface so that + * a class file is obtained from a non-standard source. + * + * @see ClassPool#insertClassPath(ClassPath) + * @see ClassPool#appendClassPath(ClassPath) + * @see ClassPool#removeClassPath(ClassPath) + */ +public interface ClassPath { + /** + * Opens a class file. + * This method may be called just to examine whether the class file + * exists as well as to read the contents of the file. + * + *

This method can return null if the specified class file is not + * found. If null is returned, the next search path is examined. + * However, if an error happens, this method must throw an exception + * so that the search will be terminated. + * + *

This method should not modify the contents of the class file. + * + * @param classname a fully-qualified class name + * @return the input stream for reading a class file + * @see javassist.Translator + */ + InputStream openClassfile(String classname) throws NotFoundException; + + /** + * Returns the uniform resource locator (URL) of the class file + * with the specified name. + * + * @param classname a fully-qualified class name. + * @return null if the specified class file could not be found. + */ + URL find(String classname); + + /** + * This method is invoked when the ClassPath object is + * detached from the search path. It will be an empty method in most of + * classes. + */ + void close(); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassPool.java b/src/main/java/com/wenshuo/agent/javassist/ClassPool.java new file mode 100644 index 0000000..2114b1f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/ClassPool.java @@ -0,0 +1,1233 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.Enumeration; + +import com.wenshuo.agent.javassist.bytecode.ClassFile; +import com.wenshuo.agent.javassist.bytecode.Descriptor; + +/** + * A container of CtClass objects. + * A CtClass object must be obtained from this object. + * If get() is called on this object, + * it searches various sources represented by ClassPath + * to find a class file and then it creates a CtClass object + * representing that class file. The created object is returned to the + * caller. + * + *

Memory consumption memo: + * + *

ClassPool objects hold all the CtClasses + * that have been created so that the consistency among modified classes + * can be guaranteed. Thus if a large number of CtClasses + * are processed, the ClassPool will consume a huge amount + * of memory. To avoid this, a ClassPool object + * should be recreated, for example, every hundred classes processed. + * Note that getDefault() is a singleton factory. + * Otherwise, detach() in CtClass should be used + * to avoid huge memory consumption. + * + *

ClassPool hierarchy: + * + *

ClassPools can make a parent-child hierarchy as + * java.lang.ClassLoaders. If a ClassPool has + * a parent pool, get() first asks the parent pool to find + * a class file. Only if the parent could not find the class file, + * get() searches the ClassPaths of + * the child ClassPool. This search order is reversed if + * ClassPath.childFirstLookup is true. + * + * @see javassist.CtClass + * @see javassist.ClassPath + */ +public class ClassPool { + // used by toClass(). + private static java.lang.reflect.Method defineClass1, defineClass2; + private static java.lang.reflect.Method definePackage; + + static { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction(){ + public Object run() throws Exception{ + Class cl = Class.forName("java.lang.ClassLoader"); + defineClass1 = cl.getDeclaredMethod("defineClass", + new Class[] { String.class, byte[].class, + int.class, int.class }); + + defineClass2 = cl.getDeclaredMethod("defineClass", + new Class[] { String.class, byte[].class, + int.class, int.class, ProtectionDomain.class }); + + definePackage = cl.getDeclaredMethod("definePackage", + new Class[] { String.class, String.class, String.class, + String.class, String.class, String.class, + String.class, java.net.URL.class }); + return null; + } + }); + } + catch (PrivilegedActionException pae) { + throw new RuntimeException("cannot initialize ClassPool", pae.getException()); + } + } + + /** + * Determines the search order. + * + *

If this field is true, get() first searches the + * class path associated to this ClassPool and then + * the class path associated with the parent ClassPool. + * Otherwise, the class path associated with the parent is searched + * first. + * + *

The default value is false. + */ + public boolean childFirstLookup = false; + + /** + * Turning the automatic pruning on/off. + * + *

If this field is true, CtClass objects are + * automatically pruned by default when toBytecode() etc. + * are called. The automatic pruning can be turned on/off individually + * for each CtClass object. + * + *

The initial value is false. + * + * @see CtClass#prune() + * @see CtClass#stopPruning(boolean) + * @see CtClass#detach() + */ + public static boolean doPruning = false; + + private int compressCount; + private static final int COMPRESS_THRESHOLD = 100; + + /* releaseUnmodifiedClassFile was introduced for avoiding a bug + of JBoss AOP. So the value should be true except for JBoss AOP. + */ + + /** + * If true, unmodified and not-recently-used class files are + * periodically released for saving memory. + * + *

The initial value is true. + */ + public static boolean releaseUnmodifiedClassFile = true; + + protected ClassPoolTail source; + protected ClassPool parent; + protected Hashtable classes; // should be synchronous + + /** + * Table of registered cflow variables. + */ + private Hashtable cflow = null; // should be synchronous. + + private static final int INIT_HASH_SIZE = 191; + + private ArrayList importedPackages; + + /** + * Creates a root class pool. No parent class pool is specified. + */ + public ClassPool() { + this(null); + } + + /** + * Creates a root class pool. If useDefaultPath is + * true, appendSystemPath() is called. Otherwise, + * this constructor is equivalent to the constructor taking no + * parameter. + * + * @param useDefaultPath true if the system search path is + * appended. + */ + public ClassPool(boolean useDefaultPath) { + this(null); + if (useDefaultPath) + appendSystemPath(); + } + + /** + * Creates a class pool. + * + * @param parent the parent of this class pool. If this is a root + * class pool, this parameter must be null. + * @see javassist.ClassPool#getDefault() + */ + public ClassPool(ClassPool parent) { + this.classes = new Hashtable(INIT_HASH_SIZE); + this.source = new ClassPoolTail(); + this.parent = parent; + if (parent == null) { + CtClass[] pt = CtClass.primitiveTypes; + for (int i = 0; i < pt.length; ++i) + classes.put(pt[i].getName(), pt[i]); + } + + this.cflow = null; + this.compressCount = 0; + clearImportedPackages(); + } + + /** + * Returns the default class pool. + * The returned object is always identical since this method is + * a singleton factory. + * + *

The default class pool searches the system search path, + * which usually includes the platform library, extension + * libraries, and the search path specified by the + * -classpath option or the CLASSPATH + * environment variable. + * + *

When this method is called for the first time, the default + * class pool is created with the following code snippet: + * + *

ClassPool cp = new ClassPool();
+     * cp.appendSystemPath();
+     * 
+ * + *

If the default class pool cannot find any class files, + * try ClassClassPath and LoaderClassPath. + * + * @see ClassClassPath + * @see LoaderClassPath + */ + public static synchronized ClassPool getDefault() { + if (defaultPool == null) { + defaultPool = new ClassPool(null); + defaultPool.appendSystemPath(); + } + + return defaultPool; + } + + private static ClassPool defaultPool = null; + + /** + * Provide a hook so that subclasses can do their own + * caching of classes. + * + * @see #cacheCtClass(String,CtClass,boolean) + * @see #removeCached(String) + */ + protected CtClass getCached(String classname) { + return (CtClass)classes.get(classname); + } + + /** + * Provides a hook so that subclasses can do their own + * caching of classes. + * + * @see #getCached(String) + * @see #removeCached(String) + */ + protected void cacheCtClass(String classname, CtClass c, boolean dynamic) { + classes.put(classname, c); + } + + /** + * Provide a hook so that subclasses can do their own + * caching of classes. + * + * @see #getCached(String) + * @see #cacheCtClass(String,CtClass,boolean) + */ + protected CtClass removeCached(String classname) { + return (CtClass)classes.remove(classname); + } + + /** + * Returns the class search path. + */ + public String toString() { + return source.toString(); + } + + /** + * This method is periodically invoked so that memory + * footprint will be minimized. + */ + void compress() { + if (compressCount++ > COMPRESS_THRESHOLD) { + compressCount = 0; + Enumeration e = classes.elements(); + while (e.hasMoreElements()) + ((CtClass)e.nextElement()).compress(); + } + } + + /** + * Record a package name so that the Javassist compiler searches + * the package to resolve a class name. + * Don't record the java.lang package, which has + * been implicitly recorded by default. + * + *

Since version 3.14, packageName can be a + * fully-qualified class name. + * + *

Note that get() in ClassPool does + * not search the recorded package. Only the compiler searches it. + * + * @param packageName the package name. + * It must not include the last '.' (dot). + * For example, "java.util" is valid but "java.util." is wrong. + * @since 3.1 + */ + public void importPackage(String packageName) { + importedPackages.add(packageName); + } + + /** + * Clear all the package names recorded by importPackage(). + * The java.lang package is not removed. + * + * @see #importPackage(String) + * @since 3.1 + */ + public void clearImportedPackages() { + importedPackages = new ArrayList(); + importedPackages.add("java.lang"); + } + + /** + * Returns all the package names recorded by importPackage(). + * + * @see #importPackage(String) + * @since 3.1 + */ + public Iterator getImportedPackages() { + return importedPackages.iterator(); + } + + /** + * Records a class name that never exists. + * For example, a package name can be recorded by this method. + * This would improve execution performance + * since get() quickly throw an exception + * without searching the class path at all + * if the given name is an invalid name recorded by this method. + * Note that searching the class path takes relatively long time. + * + *

The current implementation of this method performs nothing. + * + * @param name an invalid class name (separeted by dots). + * @deprecated + */ + public void recordInvalidClassName(String name) { + // source.recordInvalidClassName(name); + } + + /** + * Records the $cflow variable for the field specified + * by cname and fname. + * + * @param name variable name + * @param cname class name + * @param fname field name + */ + void recordCflow(String name, String cname, String fname) { + if (cflow == null) + cflow = new Hashtable(); + + cflow.put(name, new Object[] { cname, fname }); + } + + /** + * Undocumented method. Do not use; internal-use only. + * + * @param name the name of $cflow variable + */ + public Object[] lookupCflow(String name) { + if (cflow == null) + cflow = new Hashtable(); + + return (Object[])cflow.get(name); + } + + /** + * Reads a class file and constructs a CtClass + * object with a new name. + * This method is useful if you want to generate a new class as a copy + * of another class (except the class name). For example, + * + *

+     * getAndRename("Point", "Pair")
+     * 
+ * + * returns a CtClass object representing Pair + * class. The definition of Pair is the same as that of + * Point class except the class name since Pair + * is defined by reading Point.class. + * + * @param orgName the original (fully-qualified) class name + * @param newName the new class name + */ + public CtClass getAndRename(String orgName, String newName) + throws NotFoundException + { + CtClass clazz = get0(orgName, false); + if (clazz == null) + throw new NotFoundException(orgName); + + if (clazz instanceof CtClassType) + ((CtClassType)clazz).setClassPool(this); + + clazz.setName(newName); // indirectly calls + // classNameChanged() in this class + return clazz; + } + + /* + * This method is invoked by CtClassType.setName(). It removes a + * CtClass object from the hash table and inserts it with the new + * name. Don't delegate to the parent. + */ + synchronized void classNameChanged(String oldname, CtClass clazz) { + CtClass c = (CtClass)getCached(oldname); + if (c == clazz) // must check this equation. + removeCached(oldname); // see getAndRename(). + + String newName = clazz.getName(); + checkNotFrozen(newName); + cacheCtClass(newName, clazz, false); + } + + /** + * Reads a class file from the source and returns a reference + * to the CtClass + * object representing that class file. If that class file has been + * already read, this method returns a reference to the + * CtClass created when that class file was read at the + * first time. + * + *

If classname ends with "[]", then this method + * returns a CtClass object for that array type. + * + *

To obtain an inner class, use "$" instead of "." for separating + * the enclosing class name and the inner class name. + * + * @param classname a fully-qualified class name. + */ + public CtClass get(String classname) throws NotFoundException { + CtClass clazz; + if (classname == null) + clazz = null; + else + clazz = get0(classname, true); + + if (clazz == null) + throw new NotFoundException(classname); + else { + clazz.incGetCounter(); + return clazz; + } + } + + /** + * Reads a class file from the source and returns a reference + * to the CtClass + * object representing that class file. + * This method is equivalent to get except + * that it returns null when a class file is + * not found and it never throws an exception. + * + * @param classname a fully-qualified class name. + * @return a CtClass object or null. + * @see #get(String) + * @see #find(String) + * @since 3.13 + */ + public CtClass getOrNull(String classname) { + CtClass clazz = null; + if (classname == null) + clazz = null; + else + try { + /* ClassPool.get0() never throws an exception + but its subclass may implement get0 that + may throw an exception. + */ + clazz = get0(classname, true); + } + catch (NotFoundException e){} + + if (clazz != null) + clazz.incGetCounter(); + + return clazz; + } + + /** + * Returns a CtClass object with the given name. + * This is almost equivalent to get(String) except + * that classname can be an array-type "descriptor" (an encoded + * type name) such as [Ljava/lang/Object;. + * + *

Using this method is not recommended; this method should be + * used only to obtain the CtClass object + * with a name returned from getClassInfo in + * javassist.bytecode.ClassPool. getClassInfo + * returns a fully-qualified class name but, if the class is an array + * type, it returns a descriptor. + * + * @param classname a fully-qualified class name or a descriptor + * representing an array type. + * @see #get(String) + * @see javassist.bytecode.ConstPool#getClassInfo(int) + * @see javassist.bytecode.Descriptor#toCtClass(String, ClassPool) + * @since 3.8.1 + */ + public CtClass getCtClass(String classname) throws NotFoundException { + if (classname.charAt(0) == '[') + return Descriptor.toCtClass(classname, this); + else + return get(classname); + } + + /** + * @param useCache false if the cached CtClass must be ignored. + * @return null if the class could not be found. + */ + protected synchronized CtClass get0(String classname, boolean useCache) + throws NotFoundException + { + CtClass clazz = null; + if (useCache) { + clazz = getCached(classname); + if (clazz != null) + return clazz; + } + + if (!childFirstLookup && parent != null) { + clazz = parent.get0(classname, useCache); + if (clazz != null) + return clazz; + } + + clazz = createCtClass(classname, useCache); + if (clazz != null) { + // clazz.getName() != classname if classname is "[L;". + if (useCache) + cacheCtClass(clazz.getName(), clazz, false); + + return clazz; + } + + if (childFirstLookup && parent != null) + clazz = parent.get0(classname, useCache); + + return clazz; + } + + /** + * Creates a CtClass object representing the specified class. + * It first examines whether or not the corresponding class + * file exists. If yes, it creates a CtClass object. + * + * @return null if the class file could not be found. + */ + protected CtClass createCtClass(String classname, boolean useCache) { + // accept "[L;" as a class name. + if (classname.charAt(0) == '[') + classname = Descriptor.toClassName(classname); + + if (classname.endsWith("[]")) { + String base = classname.substring(0, classname.indexOf('[')); + if ((!useCache || getCached(base) == null) && find(base) == null) + return null; + else + return new CtArray(classname, this); + } + else + if (find(classname) == null) + return null; + else + return new CtClassType(classname, this); + } + + /** + * Searches the class path to obtain the URL of the class file + * specified by classname. It is also used to determine whether + * the class file exists. + * + * @param classname a fully-qualified class name. + * @return null if the class file could not be found. + * @see CtClass#getURL() + */ + public URL find(String classname) { + return source.find(classname); + } + + /* + * Is invoked by CtClassType.setName() and methods in this class. + * This method throws an exception if the class is already frozen or + * if this class pool cannot edit the class since it is in a parent + * class pool. + * + * @see checkNotExists(String) + */ + void checkNotFrozen(String classname) throws RuntimeException { + CtClass clazz = getCached(classname); + if (clazz == null) { + if (!childFirstLookup && parent != null) { + try { + clazz = parent.get0(classname, true); + } + catch (NotFoundException e) {} + if (clazz != null) + throw new RuntimeException(classname + + " is in a parent ClassPool. Use the parent."); + } + } + else + if (clazz.isFrozen()) + throw new RuntimeException(classname + + ": frozen class (cannot edit)"); + } + + /* + * This method returns null if this or its parent class pool does + * not contain a CtClass object with the class name. + * + * @see checkNotFrozen(String) + */ + CtClass checkNotExists(String classname) { + CtClass clazz = getCached(classname); + if (clazz == null) + if (!childFirstLookup && parent != null) { + try { + clazz = parent.get0(classname, true); + } + catch (NotFoundException e) {} + } + + return clazz; + } + + /* for CtClassType.getClassFile2(). Don't delegate to the parent. + */ + InputStream openClassfile(String classname) throws NotFoundException { + return source.openClassfile(classname); + } + + void writeClassfile(String classname, OutputStream out) + throws NotFoundException, IOException, CannotCompileException + { + source.writeClassfile(classname, out); + } + + /** + * Reads class files from the source and returns an array of + * CtClass + * objects representing those class files. + * + *

If an element of classnames ends with "[]", + * then this method + * returns a CtClass object for that array type. + * + * @param classnames an array of fully-qualified class name. + */ + public CtClass[] get(String[] classnames) throws NotFoundException { + if (classnames == null) + return new CtClass[0]; + + int num = classnames.length; + CtClass[] result = new CtClass[num]; + for (int i = 0; i < num; ++i) + result[i] = get(classnames[i]); + + return result; + } + + /** + * Reads a class file and obtains a compile-time method. + * + * @param classname the class name + * @param methodname the method name + * @see CtClass#getDeclaredMethod(String) + */ + public CtMethod getMethod(String classname, String methodname) + throws NotFoundException + { + CtClass c = get(classname); + return c.getDeclaredMethod(methodname); + } + + /** + * Creates a new class (or interface) from the given class file. + * If there already exists a class with the same name, the new class + * overwrites that previous class. + * + *

This method is used for creating a CtClass object + * directly from a class file. The qualified class name is obtained + * from the class file; you do not have to explicitly give the name. + * + * @param classfile class file. + * @throws RuntimeException if there is a frozen class with the + * the same name. + * @see #makeClassIfNew(InputStream) + * @see javassist.ByteArrayClassPath + */ + public CtClass makeClass(InputStream classfile) + throws IOException, RuntimeException + { + return makeClass(classfile, true); + } + + /** + * Creates a new class (or interface) from the given class file. + * If there already exists a class with the same name, the new class + * overwrites that previous class. + * + *

This method is used for creating a CtClass object + * directly from a class file. The qualified class name is obtained + * from the class file; you do not have to explicitly give the name. + * + * @param classfile class file. + * @param ifNotFrozen throws a RuntimeException if this parameter is true + * and there is a frozen class with the same name. + * @see javassist.ByteArrayClassPath + */ + public CtClass makeClass(InputStream classfile, boolean ifNotFrozen) + throws IOException, RuntimeException + { + compress(); + classfile = new BufferedInputStream(classfile); + CtClass clazz = new CtClassType(classfile, this); + clazz.checkModify(); + String classname = clazz.getName(); + if (ifNotFrozen) + checkNotFrozen(classname); + + cacheCtClass(classname, clazz, true); + return clazz; + } + + /** + * Creates a new class (or interface) from the given class file. + * If there already exists a class with the same name, the new class + * overwrites that previous class. + * + *

This method is used for creating a CtClass object + * directly from a class file. The qualified class name is obtained + * from the class file; you do not have to explicitly give the name. + * + * @param classfile class file. + * @throws RuntimeException if there is a frozen class with the + * the same name. + * @since 3.20 + */ + public CtClass makeClass(ClassFile classfile) + throws RuntimeException + { + return makeClass(classfile, true); + } + + /** + * Creates a new class (or interface) from the given class file. + * If there already exists a class with the same name, the new class + * overwrites that previous class. + * + *

This method is used for creating a CtClass object + * directly from a class file. The qualified class name is obtained + * from the class file; you do not have to explicitly give the name. + * + * @param classfile class file. + * @param ifNotFrozen throws a RuntimeException if this parameter is true + * and there is a frozen class with the same name. + * @since 3.20 + */ + public CtClass makeClass(ClassFile classfile, boolean ifNotFrozen) + throws RuntimeException + { + compress(); + CtClass clazz = new CtClassType(classfile, this); + clazz.checkModify(); + String classname = clazz.getName(); + if (ifNotFrozen) + checkNotFrozen(classname); + + cacheCtClass(classname, clazz, true); + return clazz; + } + + /** + * Creates a new class (or interface) from the given class file. + * If there already exists a class with the same name, this method + * returns the existing class; a new class is never created from + * the given class file. + * + *

This method is used for creating a CtClass object + * directly from a class file. The qualified class name is obtained + * from the class file; you do not have to explicitly give the name. + * + * @param classfile the class file. + * @see #makeClass(InputStream) + * @see javassist.ByteArrayClassPath + * @since 3.9 + */ + public CtClass makeClassIfNew(InputStream classfile) + throws IOException, RuntimeException + { + compress(); + classfile = new BufferedInputStream(classfile); + CtClass clazz = new CtClassType(classfile, this); + clazz.checkModify(); + String classname = clazz.getName(); + CtClass found = checkNotExists(classname); + if (found != null) + return found; + else { + cacheCtClass(classname, clazz, true); + return clazz; + } + } + + /** + * Creates a new public class. + * If there already exists a class with the same name, the new class + * overwrites that previous class. + * + *

If no constructor is explicitly added to the created new + * class, Javassist generates constructors and adds it when + * the class file is generated. It generates a new constructor + * for each constructor of the super class. The new constructor + * takes the same set of parameters and invokes the + * corresponding constructor of the super class. All the received + * parameters are passed to it. + * + * @param classname a fully-qualified class name. + * @throws RuntimeException if the existing class is frozen. + */ + public CtClass makeClass(String classname) throws RuntimeException { + return makeClass(classname, null); + } + + /** + * Creates a new public class. + * If there already exists a class/interface with the same name, + * the new class overwrites that previous class. + * + *

If no constructor is explicitly added to the created new + * class, Javassist generates constructors and adds it when + * the class file is generated. It generates a new constructor + * for each constructor of the super class. The new constructor + * takes the same set of parameters and invokes the + * corresponding constructor of the super class. All the received + * parameters are passed to it. + * + * @param classname a fully-qualified class name. + * @param superclass the super class. + * @throws RuntimeException if the existing class is frozen. + */ + public synchronized CtClass makeClass(String classname, CtClass superclass) + throws RuntimeException + { + checkNotFrozen(classname); + CtClass clazz = new CtNewClass(classname, this, false, superclass); + cacheCtClass(classname, clazz, true); + return clazz; + } + + /** + * Creates a new public nested class. + * This method is called by {@link CtClassType#makeNestedClass()}. + * + * @param classname a fully-qualified class name. + * @return the nested class. + */ + synchronized CtClass makeNestedClass(String classname) { + checkNotFrozen(classname); + CtClass clazz = new CtNewNestedClass(classname, this, false, null); + cacheCtClass(classname, clazz, true); + return clazz; + } + + /** + * Creates a new public interface. + * If there already exists a class/interface with the same name, + * the new interface overwrites that previous one. + * + * @param name a fully-qualified interface name. + * @throws RuntimeException if the existing interface is frozen. + */ + public CtClass makeInterface(String name) throws RuntimeException { + return makeInterface(name, null); + } + + /** + * Creates a new public interface. + * If there already exists a class/interface with the same name, + * the new interface overwrites that previous one. + * + * @param name a fully-qualified interface name. + * @param superclass the super interface. + * @throws RuntimeException if the existing interface is frozen. + */ + public synchronized CtClass makeInterface(String name, CtClass superclass) + throws RuntimeException + { + checkNotFrozen(name); + CtClass clazz = new CtNewClass(name, this, true, superclass); + cacheCtClass(name, clazz, true); + return clazz; + } + + /** + * Creates a new annotation. + * If there already exists a class/interface with the same name, + * the new interface overwrites that previous one. + * + * @param name a fully-qualified interface name. + * Or null if the annotation has no super interface. + * @throws RuntimeException if the existing interface is frozen. + * @since 3.19 + */ + public CtClass makeAnnotation(String name) throws RuntimeException { + try { + CtClass cc = makeInterface(name, get("java.lang.annotation.Annotation")); + cc.setModifiers(cc.getModifiers() | Modifier.ANNOTATION); + return cc; + } + catch (NotFoundException e) { + // should never happen. + throw new RuntimeException(e.getMessage(), e); + } + } + + /** + * Appends the system search path to the end of the + * search path. The system search path + * usually includes the platform library, extension + * libraries, and the search path specified by the + * -classpath option or the CLASSPATH + * environment variable. + * + * @return the appended class path. + */ + public ClassPath appendSystemPath() { + return source.appendSystemPath(); + } + + /** + * Insert a ClassPath object at the head of the + * search path. + * + * @return the inserted class path. + * @see javassist.ClassPath + * @see javassist.URLClassPath + * @see javassist.ByteArrayClassPath + */ + public ClassPath insertClassPath(ClassPath cp) { + return source.insertClassPath(cp); + } + + /** + * Appends a ClassPath object to the end of the + * search path. + * + * @return the appended class path. + * @see javassist.ClassPath + * @see javassist.URLClassPath + * @see javassist.ByteArrayClassPath + */ + public ClassPath appendClassPath(ClassPath cp) { + return source.appendClassPath(cp); + } + + /** + * Inserts a directory or a jar (or zip) file at the head of the + * search path. + * + * @param pathname the path name of the directory or jar file. + * It must not end with a path separator ("/"). + * If the path name ends with "/*", then all the + * jar files matching the path name are inserted. + * + * @return the inserted class path. + * @throws NotFoundException if the jar file is not found. + */ + public ClassPath insertClassPath(String pathname) + throws NotFoundException + { + return source.insertClassPath(pathname); + } + + /** + * Appends a directory or a jar (or zip) file to the end of the + * search path. + * + * @param pathname the path name of the directory or jar file. + * It must not end with a path separator ("/"). + * If the path name ends with "/*", then all the + * jar files matching the path name are appended. + * + * @return the appended class path. + * @throws NotFoundException if the jar file is not found. + */ + public ClassPath appendClassPath(String pathname) + throws NotFoundException + { + return source.appendClassPath(pathname); + } + + /** + * Detatches the ClassPath object from the search path. + * The detached ClassPath object cannot be added + * to the path again. + */ + public void removeClassPath(ClassPath cp) { + source.removeClassPath(cp); + } + + /** + * Appends directories and jar files for search. + * + *

The elements of the given path list must be separated by colons + * in Unix or semi-colons in Windows. + * + * @param pathlist a (semi)colon-separated list of + * the path names of directories and jar files. + * The directory name must not end with a path + * separator ("/"). + * @throws NotFoundException if a jar file is not found. + */ + public void appendPathList(String pathlist) throws NotFoundException { + char sep = File.pathSeparatorChar; + int i = 0; + for (;;) { + int j = pathlist.indexOf(sep, i); + if (j < 0) { + appendClassPath(pathlist.substring(i)); + break; + } + else { + appendClassPath(pathlist.substring(i, j)); + i = j + 1; + } + } + } + + /** + * Converts the given class to a java.lang.Class object. + * Once this method is called, further modifications are not + * allowed any more. + * To load the class, this method uses the context class loader + * of the current thread. It is obtained by calling + * getClassLoader(). + * + *

This behavior can be changed by subclassing the pool and changing + * the getClassLoader() method. + * If the program is running on some application + * server, the context class loader might be inappropriate to load the + * class. + * + *

This method is provided for convenience. If you need more + * complex functionality, you should write your own class loader. + * + *

Warining: A Class object returned by this method may not + * work with a security manager or a signed jar file because a + * protection domain is not specified. + * + * @see #toClass(CtClass, java.lang.ClassLoader, ProtectionDomain) + * @see #getClassLoader() + */ + public Class toClass(CtClass clazz) throws CannotCompileException { + // Some subclasses of ClassPool may override toClass(CtClass,ClassLoader). + // So we should call that method instead of toClass(.., ProtectionDomain). + return toClass(clazz, getClassLoader()); + } + + /** + * Get the classloader for toClass(), getAnnotations() in + * CtClass, etc. + * + *

The default is the context class loader. + * + * @return the classloader for the pool + * @see #toClass(CtClass) + * @see CtClass#getAnnotations() + */ + public ClassLoader getClassLoader() { + return getContextClassLoader(); + } + + /** + * Obtains a class loader that seems appropriate to look up a class + * by name. + */ + static ClassLoader getContextClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + /** + * Converts the class to a java.lang.Class object. + * Do not override this method any more at a subclass because + * toClass(CtClass) never calls this method. + * + *

Warining: A Class object returned by this method may not + * work with a security manager or a signed jar file because a + * protection domain is not specified. + * + * @deprecated Replaced by {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}. + * A subclass of ClassPool that has been + * overriding this method should be modified. It should override + * {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}. + */ + public Class toClass(CtClass ct, ClassLoader loader) + throws CannotCompileException + { + return toClass(ct, loader, null); + } + + /** + * Converts the class to a java.lang.Class object. + * Once this method is called, further modifications are not allowed + * any more. + * + *

The class file represented by the given CtClass is + * loaded by the given class loader to construct a + * java.lang.Class object. Since a private method + * on the class loader is invoked through the reflection API, + * the caller must have permissions to do that. + * + *

An easy way to obtain ProtectionDomain object is + * to call getProtectionDomain() + * in java.lang.Class. It returns the domain that the + * class belongs to. + * + *

This method is provided for convenience. If you need more + * complex functionality, you should write your own class loader. + * + * @param loader the class loader used to load this class. + * For example, the loader returned by + * getClassLoader() can be used + * for this parameter. + * @param domain the protection domain for the class. + * If it is null, the default domain created + * by java.lang.ClassLoader is used. + * + * @see #getClassLoader() + * @since 3.3 + */ + public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) + throws CannotCompileException + { + try { + byte[] b = ct.toBytecode(); + java.lang.reflect.Method method; + Object[] args; + if (domain == null) { + method = defineClass1; + args = new Object[] { ct.getName(), b, new Integer(0), + new Integer(b.length)}; + } + else { + method = defineClass2; + args = new Object[] { ct.getName(), b, new Integer(0), + new Integer(b.length), domain}; + } + + return (Class)toClass2(method, loader, args); + } + catch (RuntimeException e) { + throw e; + } + catch (java.lang.reflect.InvocationTargetException e) { + throw new CannotCompileException(e.getTargetException()); + } + catch (Exception e) { + throw new CannotCompileException(e); + } + } + + private static synchronized Object toClass2(Method method, + ClassLoader loader, Object[] args) + throws Exception + { + method.setAccessible(true); + try { + return method.invoke(loader, args); + } + finally { + method.setAccessible(false); + } + } + + /** + * Defines a new package. If the package is already defined, this method + * performs nothing. + * + *

You do not necessarily need to + * call this method. If this method is called, then + * getPackage() on the Class object returned + * by toClass() will return a non-null object. + * + * @param loader the class loader passed to toClass() or + * the default one obtained by getClassLoader(). + * @param name the package name. + * @see #getClassLoader() + * @see #toClass(CtClass) + * @see CtClass#toClass() + * @since 3.16 + */ + public void makePackage(ClassLoader loader, String name) + throws CannotCompileException + { + Object[] args = new Object[] { + name, null, null, null, null, null, null, null }; + Throwable t; + try { + toClass2(definePackage, loader, args); + return; + } + catch (java.lang.reflect.InvocationTargetException e) { + t = e.getTargetException(); + if (t == null) + t = e; + else if (t instanceof IllegalArgumentException) { + // if the package is already defined, an IllegalArgumentException + // is thrown. + return; + } + } + catch (Exception e) { + t = e; + } + + throw new CannotCompileException(t); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassPoolTail.java b/src/main/java/com/wenshuo/agent/javassist/ClassPoolTail.java new file mode 100644 index 0000000..638d201 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/ClassPoolTail.java @@ -0,0 +1,430 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.*; +import java.util.jar.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Hashtable; + +final class ClassPathList { + ClassPathList next; + ClassPath path; + + ClassPathList(ClassPath p, ClassPathList n) { + next = n; + path = p; + } +} + +final class DirClassPath implements ClassPath { + String directory; + + DirClassPath(String dirName) { + directory = dirName; + } + + public InputStream openClassfile(String classname) { + try { + char sep = File.separatorChar; + String filename = directory + sep + + classname.replace('.', sep) + ".class"; + return new FileInputStream(filename.toString()); + } + catch (FileNotFoundException e) {} + catch (SecurityException e) {} + return null; + } + + public URL find(String classname) { + char sep = File.separatorChar; + String filename = directory + sep + + classname.replace('.', sep) + ".class"; + File f = new File(filename); + if (f.exists()) + try { + return f.getCanonicalFile().toURI().toURL(); + } + catch (MalformedURLException e) {} + catch (IOException e) {} + + return null; + } + + public void close() {} + + public String toString() { + return directory; + } +} + +final class JarDirClassPath implements ClassPath { + JarClassPath[] jars; + + JarDirClassPath(String dirName) throws NotFoundException { + File[] files = new File(dirName).listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + name = name.toLowerCase(); + return name.endsWith(".jar") || name.endsWith(".zip"); + } + }); + + if (files != null) { + jars = new JarClassPath[files.length]; + for (int i = 0; i < files.length; i++) + jars[i] = new JarClassPath(files[i].getPath()); + } + } + + public InputStream openClassfile(String classname) throws NotFoundException { + if (jars != null) + for (int i = 0; i < jars.length; i++) { + InputStream is = jars[i].openClassfile(classname); + if (is != null) + return is; + } + + return null; // not found + } + + public URL find(String classname) { + if (jars != null) + for (int i = 0; i < jars.length; i++) { + URL url = jars[i].find(classname); + if (url != null) + return url; + } + + return null; // not found + } + + public void close() { + if (jars != null) + for (int i = 0; i < jars.length; i++) + jars[i].close(); + } +} + +final class JarClassPath implements ClassPath { + JarFile jarfile; + String jarfileURL; + + JarClassPath(String pathname) throws NotFoundException { + try { + jarfile = new JarFile(pathname); + jarfileURL = new File(pathname).getCanonicalFile() + .toURI().toURL().toString(); + return; + } + catch (IOException e) {} + throw new NotFoundException(pathname); + } + + public InputStream openClassfile(String classname) + throws NotFoundException + { + try { + String jarname = classname.replace('.', '/') + ".class"; + JarEntry je = jarfile.getJarEntry(jarname); + if (je != null) + return jarfile.getInputStream(je); + else + return null; // not found + } + catch (IOException e) {} + throw new NotFoundException("broken jar file?: " + + jarfile.getName()); + } + + public URL find(String classname) { + String jarname = classname.replace('.', '/') + ".class"; + JarEntry je = jarfile.getJarEntry(jarname); + if (je != null) + try { + return new URL("jar:" + jarfileURL + "!/" + jarname); + } + catch (MalformedURLException e) {} + + return null; // not found + } + + public void close() { + try { + jarfile.close(); + jarfile = null; + } + catch (IOException e) {} + } + + public String toString() { + return jarfile == null ? "" : jarfile.toString(); + } +} + +final class ClassPoolTail { + protected ClassPathList pathList; + + public ClassPoolTail() { + pathList = null; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("[class path: "); + ClassPathList list = pathList; + while (list != null) { + buf.append(list.path.toString()); + buf.append(File.pathSeparatorChar); + list = list.next; + } + + buf.append(']'); + return buf.toString(); + } + + public synchronized ClassPath insertClassPath(ClassPath cp) { + pathList = new ClassPathList(cp, pathList); + return cp; + } + + public synchronized ClassPath appendClassPath(ClassPath cp) { + ClassPathList tail = new ClassPathList(cp, null); + ClassPathList list = pathList; + if (list == null) + pathList = tail; + else { + while (list.next != null) + list = list.next; + + list.next = tail; + } + + return cp; + } + + public synchronized void removeClassPath(ClassPath cp) { + ClassPathList list = pathList; + if (list != null) + if (list.path == cp) + pathList = list.next; + else { + while (list.next != null) + if (list.next.path == cp) + list.next = list.next.next; + else + list = list.next; + } + + cp.close(); + } + + public ClassPath appendSystemPath() { + return appendClassPath(new ClassClassPath()); + } + + public ClassPath insertClassPath(String pathname) + throws NotFoundException + { + return insertClassPath(makePathObject(pathname)); + } + + public ClassPath appendClassPath(String pathname) + throws NotFoundException + { + return appendClassPath(makePathObject(pathname)); + } + + private static ClassPath makePathObject(String pathname) + throws NotFoundException + { + String lower = pathname.toLowerCase(); + if (lower.endsWith(".jar") || lower.endsWith(".zip")) + return new JarClassPath(pathname); + + int len = pathname.length(); + if (len > 2 && pathname.charAt(len - 1) == '*' + && (pathname.charAt(len - 2) == '/' + || pathname.charAt(len - 2) == File.separatorChar)) { + String dir = pathname.substring(0, len - 2); + return new JarDirClassPath(dir); + } + + return new DirClassPath(pathname); + } + + /** + * This method does not close the output stream. + */ + void writeClassfile(String classname, OutputStream out) + throws NotFoundException, IOException, CannotCompileException + { + InputStream fin = openClassfile(classname); + if (fin == null) + throw new NotFoundException(classname); + + try { + copyStream(fin, out); + } + finally { + fin.close(); + } + } + + /* + -- faster version -- + void checkClassName(String classname) throws NotFoundException { + if (find(classname) == null) + throw new NotFoundException(classname); + } + + -- slower version -- + + void checkClassName(String classname) throws NotFoundException { + InputStream fin = openClassfile(classname); + try { + fin.close(); + } + catch (IOException e) {} + } + */ + + + /** + * Opens the class file for the class specified by + * classname. + * + * @param classname a fully-qualified class name + * @return null if the file has not been found. + * @throws NotFoundException if any error is reported by ClassPath. + */ + InputStream openClassfile(String classname) + throws NotFoundException + { + ClassPathList list = pathList; + InputStream ins = null; + NotFoundException error = null; + while (list != null) { + try { + ins = list.path.openClassfile(classname); + } + catch (NotFoundException e) { + if (error == null) + error = e; + } + + if (ins == null) + list = list.next; + else + return ins; + } + + if (error != null) + throw error; + else + return null; // not found + } + + /** + * Searches the class path to obtain the URL of the class file + * specified by classname. It is also used to determine whether + * the class file exists. + * + * @param classname a fully-qualified class name. + * @return null if the class file could not be found. + */ + public URL find(String classname) { + ClassPathList list = pathList; + URL url = null; + while (list != null) { + url = list.path.find(classname); + if (url == null) + list = list.next; + else + return url; + } + + return null; + } + + /** + * Reads from an input stream until it reaches the end. + * + * @return the contents of that input stream + */ + public static byte[] readStream(InputStream fin) throws IOException { + byte[][] bufs = new byte[8][]; + int bufsize = 4096; + + for (int i = 0; i < 8; ++i) { + bufs[i] = new byte[bufsize]; + int size = 0; + int len = 0; + do { + len = fin.read(bufs[i], size, bufsize - size); + if (len >= 0) + size += len; + else { + byte[] result = new byte[bufsize - 4096 + size]; + int s = 0; + for (int j = 0; j < i; ++j) { + System.arraycopy(bufs[j], 0, result, s, s + 4096); + s = s + s + 4096; + } + + System.arraycopy(bufs[i], 0, result, s, size); + return result; + } + } while (size < bufsize); + bufsize *= 2; + } + + throw new IOException("too much data"); + } + + /** + * Reads from an input stream and write to an output stream + * until it reaches the end. This method does not close the + * streams. + */ + public static void copyStream(InputStream fin, OutputStream fout) + throws IOException + { + int bufsize = 4096; + byte[] buf = null; + for (int i = 0; i < 64; ++i) { + if (i < 8) { + bufsize *= 2; + buf = new byte[bufsize]; + } + int size = 0; + int len = 0; + do { + len = fin.read(buf, size, bufsize - size); + if (len >= 0) + size += len; + else { + fout.write(buf, 0, size); + return; + } + } while (size < bufsize); + fout.write(buf); + } + + throw new IOException("too much data"); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CodeConverter.java b/src/main/java/com/wenshuo/agent/javassist/CodeConverter.java new file mode 100644 index 0000000..5bd3014 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CodeConverter.java @@ -0,0 +1,809 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.convert.*; + +/** + * Simple translator of method bodies + * (also see the javassist.expr package). + * + *

Instances of this class specifies how to instrument of the + * bytecodes representing a method body. They are passed to + * CtClass.instrument() or + * CtMethod.instrument() as a parameter. + * + *

Example: + *

+ * ClassPool cp = ClassPool.getDefault();
+ * CtClass point = cp.get("Point");
+ * CtClass singleton = cp.get("Singleton");
+ * CtClass client = cp.get("Client");
+ * CodeConverter conv = new CodeConverter();
+ * conv.replaceNew(point, singleton, "makePoint");
+ * client.instrument(conv);
+ * 
+ * + *

This program substitutes "Singleton.makePoint()" + * for all occurrences of "new Point()" + * appearing in methods declared in a Client class. + * + * @see javassist.CtClass#instrument(CodeConverter) + * @see javassist.CtMethod#instrument(CodeConverter) + * @see javassist.expr.ExprEditor + */ +public class CodeConverter { + protected Transformer transformers = null; + + /** + * Modify a method body so that instantiation of the specified class + * is replaced with a call to the specified static method. For example, + * replaceNew(ctPoint, ctSingleton, "createPoint") + * (where ctPoint and ctSingleton are + * compile-time classes for class Point and class + * Singleton, respectively) + * replaces all occurrences of: + * + *

new Point(x, y)
+ * + * in the method body with: + * + *
Singleton.createPoint(x, y)
+ * + *

This enables to intercept instantiation of Point + * and change the samentics. For example, the following + * createPoint() implements the singleton pattern: + * + *

public static Point createPoint(int x, int y) {
+     *     if (aPoint == null)
+     *         aPoint = new Point(x, y);
+     *     return aPoint;
+     * }
+     * 
+ * + *

The static method call substituted for the original new + * expression must be + * able to receive the same set of parameters as the original + * constructor. If there are multiple constructors with different + * parameter types, then there must be multiple static methods + * with the same name but different parameter types. + * + *

The return type of the substituted static method must be + * the exactly same as the type of the instantiated class specified by + * newClass. + * + * @param newClass the instantiated class. + * @param calledClass the class in which the static method is + * declared. + * @param calledMethod the name of the static method. + */ + public void replaceNew(CtClass newClass, + CtClass calledClass, String calledMethod) { + transformers = new TransformNew(transformers, newClass.getName(), + calledClass.getName(), calledMethod); + } + + /** + * Modify a method body so that instantiation of the class + * specified by oldClass + * is replaced with instantiation of another class newClass. + * For example, + * replaceNew(ctPoint, ctPoint2) + * (where ctPoint and ctPoint2 are + * compile-time classes for class Point and class + * Point2, respectively) + * replaces all occurrences of: + * + *

new Point(x, y)
+ * + * in the method body with: + * + *
new Point2(x, y)
+ * + *

Note that Point2 must be type-compatible with Point. + * It must have the same set of methods, fields, and constructors as the + * replaced class. + */ + public void replaceNew(CtClass oldClass, CtClass newClass) { + transformers = new TransformNewClass(transformers, oldClass.getName(), + newClass.getName()); + } + + /** + * Modify a method body so that field read/write expressions access + * a different field from the original one. + * + *

Note that this method changes only the filed name and the class + * declaring the field; the type of the target object does not change. + * Therefore, the substituted field must be declared in the same class + * or a superclass of the original class. + * + *

Also, clazz and newClass must specify + * the class directly declaring the field. They must not specify + * a subclass of that class. + * + * @param field the originally accessed field. + * @param newClass the class declaring the substituted field. + * @param newFieldname the name of the substituted field. + */ + public void redirectFieldAccess(CtField field, + CtClass newClass, String newFieldname) { + transformers = new TransformFieldAccess(transformers, field, + newClass.getName(), + newFieldname); + } + + /** + * Modify a method body so that an expression reading the specified + * field is replaced with a call to the specified static method. + * This static method receives the target object of the original + * read expression as a parameter. It must return a value of + * the same type as the field. + * + *

For example, the program below + * + *

Point p = new Point();
+     * int newX = p.x + 3;
+ * + *

can be translated into: + * + *

Point p = new Point();
+     * int newX = Accessor.readX(p) + 3;
+ * + *

where + * + *

public class Accessor {
+     *     public static int readX(Object target) { ... }
+     * }
+ * + *

The type of the parameter of readX() must + * be java.lang.Object independently of the actual + * type of target. The return type must be the same + * as the field type. + * + * @param field the field. + * @param calledClass the class in which the static method is + * declared. + * @param calledMethod the name of the static method. + */ + public void replaceFieldRead(CtField field, + CtClass calledClass, String calledMethod) { + transformers = new TransformReadField(transformers, field, + calledClass.getName(), + calledMethod); + } + + /** + * Modify a method body so that an expression writing the specified + * field is replaced with a call to the specified static method. + * This static method receives two parameters: the target object of + * the original + * write expression and the assigned value. The return type of the + * static method is void. + * + *

For example, the program below + * + *

Point p = new Point();
+     * p.x = 3;
+ * + *

can be translated into: + * + *

Point p = new Point();
+     * Accessor.writeX(3);
+ * + *

where + * + *

public class Accessor {
+     *     public static void writeX(Object target, int value) { ... }
+     * }
+ * + *

The type of the first parameter of writeX() must + * be java.lang.Object independently of the actual + * type of target. The type of the second parameter + * is the same as the field type. + * + * @param field the field. + * @param calledClass the class in which the static method is + * declared. + * @param calledMethod the name of the static method. + */ + public void replaceFieldWrite(CtField field, + CtClass calledClass, String calledMethod) { + transformers = new TransformWriteField(transformers, field, + calledClass.getName(), + calledMethod); + } + + /** + * Modify a method body, so that ALL accesses to an array are replaced with + * calls to static methods within another class. In the case of reading an + * element from the array, this is replaced with a call to a static method with + * the array and the index as arguments, the return value is the value read from + * the array. If writing to an array, this is replaced with a call to a static + * method with the array, index and new value as parameters, the return value of + * the static method is void. + * + *

The calledClass parameter is the class containing the static methods to be used + * for array replacement. The names parameter points to an implementation of + * ArrayAccessReplacementMethodNames which specifies the names of the method to be + * used for access for each type of array. For example reading from an int[] will + * require a different method than if writing to an int[], and writing to a long[] + * will require a different method than if writing to a byte[]. If the implementation + * of ArrayAccessReplacementMethodNames does not contain the name for access for a + * type of array, that access is not replaced. + * + *

A default implementation of ArrayAccessReplacementMethodNames called + * DefaultArrayAccessReplacementMethodNames has been provided and is what is used in the + * following example. This also assumes that 'foo.ArrayAdvisor' is the name of the + * CtClass passed in. + * + *

If we have the following class: + *

class POJO{
+     *    int[] ints = new int[]{1, 2, 3, 4, 5};
+     *    long[] longs = new int[]{10, 20, 30};
+     *    Object objects = new Object[]{true, false};
+     *    Integer[] integers = new Integer[]{new Integer(10)};
+     * }
+     * 
+ * and this is accessed as: + *
POJO p = new POJO();
+     * 
+     * //Write to int array
+     * p.ints[2] = 7;
+     * 
+     * //Read from int array
+     * int i = p.ints[2];
+     * 
+     * //Write to long array
+     * p.longs[2] = 1000L;
+     * 
+     * //Read from long array
+     * long l = p.longs[2];
+     * 
+     * //Write to Object array
+     * p.objects[2] = "Hello";
+     * 
+     * //Read from Object array
+     * Object o = p.objects[2];
+     * 
+     * //Write to Integer array
+     * Integer integer = new Integer(5);
+     * p.integers[0] = integer;
+     * 
+     * //Read from Object array
+     * integer = p.integers[0];
+     * 
+ * + * Following instrumentation we will have + *
POJO p = new POJO();
+     * 
+     * //Write to int array
+     * ArrayAdvisor.arrayWriteInt(p.ints, 2, 7);
+     * 
+     * //Read from int array
+     * int i = ArrayAdvisor.arrayReadInt(p.ints, 2);
+     * 
+     * //Write to long array
+     * ArrayAdvisor.arrayWriteLong(p.longs, 2, 1000L);
+     * 
+     * //Read from long array
+     * long l = ArrayAdvisor.arrayReadLong(p.longs, 2);
+     * 
+     * //Write to Object array
+     * ArrayAdvisor.arrayWriteObject(p.objects, 2, "Hello");
+     * 
+     * //Read from Object array
+     * Object o = ArrayAdvisor.arrayReadObject(p.objects, 2);
+     * 
+     * //Write to Integer array
+     * Integer integer = new Integer(5);
+     * ArrayAdvisor.arrayWriteObject(p.integers, 0, integer);
+     * 
+     * //Read from Object array
+     * integer = ArrayAdvisor.arrayWriteObject(p.integers, 0);
+     * 
+ * + * @see DefaultArrayAccessReplacementMethodNames + * + * @param calledClass the class containing the static methods. + * @param names contains the names of the methods to replace + * the different kinds of array access with. + */ + public void replaceArrayAccess(CtClass calledClass, ArrayAccessReplacementMethodNames names) + throws NotFoundException + { + transformers = new TransformAccessArrayField(transformers, calledClass.getName(), names); + } + + /** + * Modify method invocations in a method body so that a different + * method will be invoked. + * + *

Note that the target object, the parameters, or + * the type of invocation + * (static method call, interface call, or private method call) + * are not modified. Only the method name is changed. The substituted + * method must have the same signature that the original one has. + * If the original method is a static method, the substituted method + * must be static. + * + * @param origMethod original method + * @param substMethod substituted method + */ + public void redirectMethodCall(CtMethod origMethod, + CtMethod substMethod) + throws CannotCompileException + { + String d1 = origMethod.getMethodInfo2().getDescriptor(); + String d2 = substMethod.getMethodInfo2().getDescriptor(); + if (!d1.equals(d2)) + throw new CannotCompileException("signature mismatch: " + + substMethod.getLongName()); + + int mod1 = origMethod.getModifiers(); + int mod2 = substMethod.getModifiers(); + if (Modifier.isStatic(mod1) != Modifier.isStatic(mod2) + || (Modifier.isPrivate(mod1) && !Modifier.isPrivate(mod2)) + || origMethod.getDeclaringClass().isInterface() + != substMethod.getDeclaringClass().isInterface()) + throw new CannotCompileException("invoke-type mismatch " + + substMethod.getLongName()); + + transformers = new TransformCall(transformers, origMethod, + substMethod); + } + + /** + * Correct invocations to a method that has been renamed. + * If a method is renamed, calls to that method must be also + * modified so that the method with the new name will be called. + * + *

The method must be declared in the same class before and + * after it is renamed. + * + *

Note that the target object, the parameters, or + * the type of invocation + * (static method call, interface call, or private method call) + * are not modified. Only the method name is changed. + * + * @param oldMethodName the old name of the method. + * @param newMethod the method with the new name. + * @see javassist.CtMethod#setName(String) + */ + public void redirectMethodCall(String oldMethodName, + CtMethod newMethod) + throws CannotCompileException + { + transformers + = new TransformCall(transformers, oldMethodName, newMethod); + } + + /** + * Insert a call to another method before an existing method call. + * That "before" method must be static. The return type must be + * void. As parameters, the before method receives + * the target object and all the parameters to the originally invoked + * method. For example, if the originally invoked method is + * move(): + * + *

class Point {
+     *     Point move(int x, int y) { ... }
+     * }
+ * + *

Then the before method must be something like this: + * + *

class Verbose {
+     *     static void print(Point target, int x, int y) { ... }
+     * }
+ * + *

The CodeConverter would translate bytecode + * equivalent to: + * + *

Point p2 = p.move(x + y, 0);
+ * + *

into the bytecode equivalent to: + * + *

int tmp1 = x + y;
+     * int tmp2 = 0;
+     * Verbose.print(p, tmp1, tmp2);
+     * Point p2 = p.move(tmp1, tmp2);
+ * + * @param origMethod the method originally invoked. + * @param beforeMethod the method invoked before + * origMethod. + */ + public void insertBeforeMethod(CtMethod origMethod, + CtMethod beforeMethod) + throws CannotCompileException + { + try { + transformers = new TransformBefore(transformers, origMethod, + beforeMethod); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + } + + /** + * Inserts a call to another method after an existing method call. + * That "after" method must be static. The return type must be + * void. As parameters, the after method receives + * the target object and all the parameters to the originally invoked + * method. For example, if the originally invoked method is + * move(): + * + *
class Point {
+     *     Point move(int x, int y) { ... }
+     * }
+ * + *

Then the after method must be something like this: + * + *

class Verbose {
+     *     static void print(Point target, int x, int y) { ... }
+     * }
+ * + *

The CodeConverter would translate bytecode + * equivalent to: + * + *

Point p2 = p.move(x + y, 0);
+ * + *

into the bytecode equivalent to: + * + *

+     * int tmp1 = x + y;
+     * int tmp2 = 0;
+     * Point p2 = p.move(tmp1, tmp2);
+     * Verbose.print(p, tmp1, tmp2);
+ * + * @param origMethod the method originally invoked. + * @param afterMethod the method invoked after + * origMethod. + */ + public void insertAfterMethod(CtMethod origMethod, + CtMethod afterMethod) + throws CannotCompileException + { + try { + transformers = new TransformAfter(transformers, origMethod, + afterMethod); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + } + + /** + * Performs code conversion. + */ + protected void doit(CtClass clazz, MethodInfo minfo, ConstPool cp) + throws CannotCompileException + { + Transformer t; + CodeAttribute codeAttr = minfo.getCodeAttribute(); + if (codeAttr == null || transformers == null) + return; + for (t = transformers; t != null; t = t.getNext()) + t.initialize(cp, clazz, minfo); + + CodeIterator iterator = codeAttr.iterator(); + while (iterator.hasNext()) { + try { + int pos = iterator.next(); + for (t = transformers; t != null; t = t.getNext()) + pos = t.transform(clazz, pos, iterator, cp); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + int locals = 0; + int stack = 0; + for (t = transformers; t != null; t = t.getNext()) { + int s = t.extraLocals(); + if (s > locals) + locals = s; + + s = t.extraStack(); + if (s > stack) + stack = s; + } + + for (t = transformers; t != null; t = t.getNext()) + t.clean(); + + if (locals > 0) + codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals); + + if (stack > 0) + codeAttr.setMaxStack(codeAttr.getMaxStack() + stack); + + try { + minfo.rebuildStackMapIf6(clazz.getClassPool(), + clazz.getClassFile2()); + } + catch (BadBytecode b) { + throw new CannotCompileException(b.getMessage(), b); + } + } + + /** + * Interface containing the method names to be used + * as array access replacements. + * + * @author Kabir Khan + * @version $Revision: 1.16 $ + */ + public interface ArrayAccessReplacementMethodNames + { + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;I)B to replace reading from a byte[]. + */ + String byteOrBooleanRead(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;IB)V to replace writing to a byte[]. + */ + String byteOrBooleanWrite(); + + /** + * @return the name of a static method with the signature + * (Ljava/lang/Object;I)C to replace reading from a char[]. + */ + String charRead(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;IC)V to replace writing to a byte[]. + */ + String charWrite(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;I)D to replace reading from a double[]. + */ + String doubleRead(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;ID)V to replace writing to a double[]. + */ + String doubleWrite(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;I)F to replace reading from a float[]. + */ + String floatRead(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;IF)V to replace writing to a float[]. + */ + String floatWrite(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;I)I to replace reading from a int[]. + */ + String intRead(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;II)V to replace writing to a int[]. + */ + String intWrite(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;I)J to replace reading from a long[]. + */ + String longRead(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;IJ)V to replace writing to a long[]. + */ + String longWrite(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;I)Ljava/lang/Object; + * to replace reading from a Object[] (or any subclass of object). + */ + String objectRead(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;ILjava/lang/Object;)V + * to replace writing to a Object[] (or any subclass of object). + */ + String objectWrite(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;I)S to replace reading from a short[]. + */ + String shortRead(); + + /** + * Returns the name of a static method with the signature + * (Ljava/lang/Object;IS)V to replace writing to a short[]. + */ + String shortWrite(); + } + + /** + * Default implementation of the ArrayAccessReplacementMethodNames + * interface giving default values for method names to be used for replacing + * accesses to array elements. + * + * @author Kabir Khan + * @version $Revision: 1.16 $ + */ + public static class DefaultArrayAccessReplacementMethodNames + implements ArrayAccessReplacementMethodNames + { + /** + * Returns "arrayReadByteOrBoolean" as the name of the static method with the signature + * (Ljava/lang/Object;I)B to replace reading from a byte[]. + */ + public String byteOrBooleanRead() + { + return "arrayReadByteOrBoolean"; + } + + /** + * Returns "arrayWriteByteOrBoolean" as the name of the static method with the signature + * (Ljava/lang/Object;IB)V to replace writing to a byte[]. + */ + public String byteOrBooleanWrite() + { + return "arrayWriteByteOrBoolean"; + } + + /** + * Returns "arrayReadChar" as the name of the static method with the signature + * (Ljava/lang/Object;I)C to replace reading from a char[]. + */ + public String charRead() + { + return "arrayReadChar"; + } + + /** + * Returns "arrayWriteChar" as the name of the static method with the signature + * (Ljava/lang/Object;IC)V to replace writing to a byte[]. + */ + public String charWrite() + { + return "arrayWriteChar"; + } + + /** + * Returns "arrayReadDouble" as the name of the static method with the signature + * (Ljava/lang/Object;I)D to replace reading from a double[]. + */ + public String doubleRead() + { + return "arrayReadDouble"; + } + + /** + * Returns "arrayWriteDouble" as the name of the static method with the signature + * (Ljava/lang/Object;ID)V to replace writing to a double[]. + */ + public String doubleWrite() + { + return "arrayWriteDouble"; + } + + /** + * Returns "arrayReadFloat" as the name of the static method with the signature + * (Ljava/lang/Object;I)F to replace reading from a float[]. + */ + public String floatRead() + { + return "arrayReadFloat"; + } + + /** + * Returns "arrayWriteFloat" as the name of the static method with the signature + * (Ljava/lang/Object;IF)V to replace writing to a float[]. + */ + public String floatWrite() + { + return "arrayWriteFloat"; + } + + /** + * Returns "arrayReadInt" as the name of the static method with the signature + * (Ljava/lang/Object;I)I to replace reading from a int[]. + */ + public String intRead() + { + return "arrayReadInt"; + } + + /** + * Returns "arrayWriteInt" as the name of the static method with the signature + * (Ljava/lang/Object;II)V to replace writing to a int[]. + */ + public String intWrite() + { + return "arrayWriteInt"; + } + + /** + * Returns "arrayReadLong" as the name of the static method with the signature + * (Ljava/lang/Object;I)J to replace reading from a long[]. + */ + public String longRead() + { + return "arrayReadLong"; + } + + /** + * Returns "arrayWriteLong" as the name of the static method with the signature + * (Ljava/lang/Object;IJ)V to replace writing to a long[]. + */ + public String longWrite() + { + return "arrayWriteLong"; + } + + /** + * Returns "arrayReadObject" as the name of the static method with the signature + * (Ljava/lang/Object;I)Ljava/lang/Object; to replace reading from a Object[] (or any subclass of object). + */ + public String objectRead() + { + return "arrayReadObject"; + } + + /** + * Returns "arrayWriteObject" as the name of the static method with the signature + * (Ljava/lang/Object;ILjava/lang/Object;)V to replace writing to a Object[] (or any subclass of object). + */ + public String objectWrite() + { + return "arrayWriteObject"; + } + + /** + * Returns "arrayReadShort" as the name of the static method with the signature + * (Ljava/lang/Object;I)S to replace reading from a short[]. + */ + public String shortRead() + { + return "arrayReadShort"; + } + + /** + * Returns "arrayWriteShort" as the name of the static method with the signature + * (Ljava/lang/Object;IS)V to replace writing to a short[]. + */ + public String shortWrite() + { + return "arrayWriteShort"; + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtArray.java b/src/main/java/com/wenshuo/agent/javassist/CtArray.java new file mode 100644 index 0000000..3864c6a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtArray.java @@ -0,0 +1,113 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +/** + * Array types. + */ +final class CtArray extends CtClass { + protected ClassPool pool; + + // the name of array type ends with "[]". + CtArray(String name, ClassPool cp) { + super(name); + pool = cp; + } + + public ClassPool getClassPool() { + return pool; + } + + public boolean isArray() { + return true; + } + + private CtClass[] interfaces = null; + + public int getModifiers() { + int mod = Modifier.FINAL; + try { + mod |= getComponentType().getModifiers() + & (Modifier.PROTECTED | Modifier.PUBLIC | Modifier.PRIVATE); + } + catch (NotFoundException e) {} + return mod; + } + + public CtClass[] getInterfaces() throws NotFoundException { + if (interfaces == null) { + Class[] intfs = Object[].class.getInterfaces(); + // java.lang.Cloneable and java.io.Serializable. + // If the JVM is CLDC, intfs is empty. + interfaces = new CtClass[intfs.length]; + for (int i = 0; i < intfs.length; i++) + interfaces[i] = pool.get(intfs[i].getName()); + } + + return interfaces; + } + + public boolean subtypeOf(CtClass clazz) throws NotFoundException { + if (super.subtypeOf(clazz)) + return true; + + String cname = clazz.getName(); + if (cname.equals(javaLangObject)) + return true; + + CtClass[] intfs = getInterfaces(); + for (int i = 0; i < intfs.length; i++) + if (intfs[i].subtypeOf(clazz)) + return true; + + return clazz.isArray() + && getComponentType().subtypeOf(clazz.getComponentType()); + } + + public CtClass getComponentType() throws NotFoundException { + String name = getName(); + return pool.get(name.substring(0, name.length() - 2)); + } + + public CtClass getSuperclass() throws NotFoundException { + return pool.get(javaLangObject); + } + + public CtMethod[] getMethods() { + try { + return getSuperclass().getMethods(); + } + catch (NotFoundException e) { + return super.getMethods(); + } + } + + public CtMethod getMethod(String name, String desc) + throws NotFoundException + { + return getSuperclass().getMethod(name, desc); + } + + public CtConstructor[] getConstructors() { + try { + return getSuperclass().getConstructors(); + } + catch (NotFoundException e) { + return super.getConstructors(); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtBehavior.java b/src/main/java/com/wenshuo/agent/javassist/CtBehavior.java new file mode 100644 index 0000000..ff03e03 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtBehavior.java @@ -0,0 +1,1219 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.Javac; +import com.wenshuo.agent.javassist.compiler.CompileError; +import com.wenshuo.agent.javassist.expr.ExprEditor; + +/** + * CtBehavior represents a method, a constructor, + * or a static constructor (class initializer). + * It is the abstract super class of + * CtMethod and CtConstructor. + * + *

To directly read or modify bytecode, obtain MethodInfo + * objects. + * + * @see #getMethodInfo() + */ +public abstract class CtBehavior extends CtMember { + protected MethodInfo methodInfo; + + protected CtBehavior(CtClass clazz, MethodInfo minfo) { + super(clazz); + methodInfo = minfo; + } + + /** + * @param isCons true if this is a constructor. + */ + void copy(CtBehavior src, boolean isCons, ClassMap map) + throws CannotCompileException + { + CtClass declaring = declaringClass; + MethodInfo srcInfo = src.methodInfo; + CtClass srcClass = src.getDeclaringClass(); + ConstPool cp = declaring.getClassFile2().getConstPool(); + + map = new ClassMap(map); + map.put(srcClass.getName(), declaring.getName()); + try { + boolean patch = false; + CtClass srcSuper = srcClass.getSuperclass(); + CtClass destSuper = declaring.getSuperclass(); + String destSuperName = null; + if (srcSuper != null && destSuper != null) { + String srcSuperName = srcSuper.getName(); + destSuperName = destSuper.getName(); + if (!srcSuperName.equals(destSuperName)) + if (srcSuperName.equals(CtClass.javaLangObject)) + patch = true; + else + map.putIfNone(srcSuperName, destSuperName); + } + + // a stack map table is copied from srcInfo. + methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map); + if (isCons && patch) + methodInfo.setSuperclass(destSuperName); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + protected void extendToString(StringBuffer buffer) { + buffer.append(' '); + buffer.append(getName()); + buffer.append(' '); + buffer.append(methodInfo.getDescriptor()); + } + + /** + * Returns the method or constructor name followed by parameter types + * such as javassist.CtBehavior.stBody(String). + * + * @since 3.5 + */ + public abstract String getLongName(); + + /** + * Returns the MethodInfo representing this method/constructor in the + * class file. + * + *

If you modify the bytecode through the returned + * MethodInfo object, you might have to explicitly + * rebuild a stack map table. Javassist does not automatically + * rebuild it for avoiding unnecessary rebuilding. + * + * @see javassist.bytecode.MethodInfo#rebuildStackMap(ClassPool) + */ + public MethodInfo getMethodInfo() { + declaringClass.checkModify(); + return methodInfo; + } + + /** + * Returns the MethodInfo representing the method/constructor in the + * class file (read only). + * Normal applications do not need calling this method. Use + * getMethodInfo(). + * + *

The MethodInfo object obtained by this method + * is read only. Changes to this object might not be reflected + * on a class file generated by toBytecode(), + * toClass(), etc in CtClass. + * + *

This method is available even if the CtClass + * containing this method is frozen. However, if the class is + * frozen, the MethodInfo might be also pruned. + * + * @see #getMethodInfo() + * @see CtClass#isFrozen() + * @see CtClass#prune() + */ + public MethodInfo getMethodInfo2() { return methodInfo; } + + /** + * Obtains the modifiers of the method/constructor. + * + * @return modifiers encoded with + * javassist.Modifier. + * @see Modifier + */ + public int getModifiers() { + return AccessFlag.toModifier(methodInfo.getAccessFlags()); + } + + /** + * Sets the encoded modifiers of the method/constructor. + * + *

Changing the modifiers may cause a problem. + * For example, if a non-static method is changed to static, + * the method will be rejected by the bytecode verifier. + * + * @see Modifier + */ + public void setModifiers(int mod) { + declaringClass.checkModify(); + methodInfo.setAccessFlags(AccessFlag.of(mod)); + } + + /** + * Returns true if the class has the specified annotation class. + * + * @param clz the annotation class. + * @return true if the annotation is found, + * otherwise false. + * @since 3.11 + */ + public boolean hasAnnotation(Class clz) { + MethodInfo mi = getMethodInfo2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + mi.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + mi.getAttribute(AnnotationsAttribute.visibleTag); + return CtClassType.hasAnnotationType(clz, + getDeclaringClass().getClassPool(), + ainfo, ainfo2); + } + + /** + * Returns the annotation if the class has the specified annotation class. + * For example, if an annotation @Author is associated + * with this method/constructor, an Author object is returned. + * The member values can be obtained by calling methods on + * the Author object. + * + * @param clz the annotation class. + * @return the annotation if found, otherwise null. + * @since 3.11 + */ + public Object getAnnotation(Class clz) throws ClassNotFoundException { + MethodInfo mi = getMethodInfo2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + mi.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + mi.getAttribute(AnnotationsAttribute.visibleTag); + return CtClassType.getAnnotationType(clz, + getDeclaringClass().getClassPool(), + ainfo, ainfo2); + } + + /** + * Returns the annotations associated with this method or constructor. + * + * @return an array of annotation-type objects. + * @see #getAvailableAnnotations() + * @since 3.1 + */ + public Object[] getAnnotations() throws ClassNotFoundException { + return getAnnotations(false); + } + + /** + * Returns the annotations associated with this method or constructor. + * If any annotations are not on the classpath, they are not included + * in the returned array. + * + * @return an array of annotation-type objects. + * @see #getAnnotations() + * @since 3.3 + */ + public Object[] getAvailableAnnotations(){ + try{ + return getAnnotations(true); + } + catch (ClassNotFoundException e){ + throw new RuntimeException("Unexpected exception", e); + } + } + + private Object[] getAnnotations(boolean ignoreNotFound) + throws ClassNotFoundException + { + MethodInfo mi = getMethodInfo2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + mi.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + mi.getAttribute(AnnotationsAttribute.visibleTag); + return CtClassType.toAnnotationType(ignoreNotFound, + getDeclaringClass().getClassPool(), + ainfo, ainfo2); + } + + /** + * Returns the parameter annotations associated with this method or constructor. + * + * @return an array of annotation-type objects. The length of the returned array is + * equal to the number of the formal parameters. If each parameter has no + * annotation, the elements of the returned array are empty arrays. + * + * @see #getAvailableParameterAnnotations() + * @see #getAnnotations() + * @since 3.1 + */ + public Object[][] getParameterAnnotations() throws ClassNotFoundException { + return getParameterAnnotations(false); + } + + /** + * Returns the parameter annotations associated with this method or constructor. + * If any annotations are not on the classpath, they are not included in the + * returned array. + * + * @return an array of annotation-type objects. The length of the returned array is + * equal to the number of the formal parameters. If each parameter has no + * annotation, the elements of the returned array are empty arrays. + * + * @see #getParameterAnnotations() + * @see #getAvailableAnnotations() + * @since 3.3 + */ + public Object[][] getAvailableParameterAnnotations(){ + try { + return getParameterAnnotations(true); + } + catch(ClassNotFoundException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + + Object[][] getParameterAnnotations(boolean ignoreNotFound) + throws ClassNotFoundException + { + MethodInfo mi = getMethodInfo2(); + ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute) + mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag); + ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute) + mi.getAttribute(ParameterAnnotationsAttribute.visibleTag); + return CtClassType.toAnnotationType(ignoreNotFound, + getDeclaringClass().getClassPool(), + ainfo, ainfo2, mi); + } + + /** + * Obtains parameter types of this method/constructor. + */ + public CtClass[] getParameterTypes() throws NotFoundException { + return Descriptor.getParameterTypes(methodInfo.getDescriptor(), + declaringClass.getClassPool()); + } + + /** + * Obtains the type of the returned value. + */ + CtClass getReturnType0() throws NotFoundException { + return Descriptor.getReturnType(methodInfo.getDescriptor(), + declaringClass.getClassPool()); + } + + /** + * Returns the method signature (the parameter types + * and the return type). + * The method signature is represented by a character string + * called method descriptor, which is defined in the JVM specification. + * If two methods/constructors have + * the same parameter types + * and the return type, getSignature() returns the + * same string (the return type of constructors is void). + * + *

Note that the returned string is not the type signature + * contained in the SignatureAttirbute. It is + * a descriptor. + * + * @see javassist.bytecode.Descriptor + * @see #getGenericSignature() + */ + public String getSignature() { + return methodInfo.getDescriptor(); + } + + /** + * Returns the generic signature of the method. + * It represents parameter types including type variables. + * + * @see SignatureAttribute#toMethodSignature(String) + * @since 3.17 + */ + public String getGenericSignature() { + SignatureAttribute sa + = (SignatureAttribute)methodInfo.getAttribute(SignatureAttribute.tag); + return sa == null ? null : sa.getSignature(); + } + + /** + * Set the generic signature of the method. + * It represents parameter types including type variables. + * See {@link javassist.CtClass#setGenericSignature(String)} + * for a code sample. + * + * @param sig a new generic signature. + * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode() + * @since 3.17 + */ + public void setGenericSignature(String sig) { + declaringClass.checkModify(); + methodInfo.addAttribute(new SignatureAttribute(methodInfo.getConstPool(), sig)); + } + + /** + * Obtains exceptions that this method/constructor may throw. + * + * @return a zero-length array if there is no throws clause. + */ + public CtClass[] getExceptionTypes() throws NotFoundException { + String[] exceptions; + ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); + if (ea == null) + exceptions = null; + else + exceptions = ea.getExceptions(); + + return declaringClass.getClassPool().get(exceptions); + } + + /** + * Sets exceptions that this method/constructor may throw. + */ + public void setExceptionTypes(CtClass[] types) throws NotFoundException { + declaringClass.checkModify(); + if (types == null || types.length == 0) { + methodInfo.removeExceptionsAttribute(); + return; + } + + String[] names = new String[types.length]; + for (int i = 0; i < types.length; ++i) + names[i] = types[i].getName(); + + ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); + if (ea == null) { + ea = new ExceptionsAttribute(methodInfo.getConstPool()); + methodInfo.setExceptionsAttribute(ea); + } + + ea.setExceptions(names); + } + + /** + * Returns true if the body is empty. + */ + public abstract boolean isEmpty(); + + /** + * Sets a method/constructor body. + * + * @param src the source code representing the body. + * It must be a single statement or block. + * If it is null, the substituted + * body does nothing except returning zero or null. + */ + public void setBody(String src) throws CannotCompileException { + setBody(src, null, null); + } + + /** + * Sets a method/constructor body. + * + * @param src the source code representing the body. + * It must be a single statement or block. + * If it is null, the substituted + * body does nothing except returning zero or null. + * @param delegateObj the source text specifying the object + * that is called on by $proceed(). + * @param delegateMethod the name of the method + * that is called by $proceed(). + */ + public void setBody(String src, + String delegateObj, String delegateMethod) + throws CannotCompileException + { + CtClass cc = declaringClass; + cc.checkModify(); + try { + Javac jv = new Javac(cc); + if (delegateMethod != null) + jv.recordProceed(delegateObj, delegateMethod); + + Bytecode b = jv.compileBody(this, src); + methodInfo.setCodeAttribute(b.toCodeAttribute()); + methodInfo.setAccessFlags(methodInfo.getAccessFlags() + & ~AccessFlag.ABSTRACT); + methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); + declaringClass.rebuildClassFile(); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + static void setBody0(CtClass srcClass, MethodInfo srcInfo, + CtClass destClass, MethodInfo destInfo, + ClassMap map) + throws CannotCompileException + { + destClass.checkModify(); + + map = new ClassMap(map); + map.put(srcClass.getName(), destClass.getName()); + try { + CodeAttribute cattr = srcInfo.getCodeAttribute(); + if (cattr != null) { + ConstPool cp = destInfo.getConstPool(); + CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map); + destInfo.setCodeAttribute(ca); + // a stack map table is copied to destInfo. + } + } + catch (CodeAttribute.RuntimeCopyException e) { + /* the exception may be thrown by copy() in CodeAttribute. + */ + throw new CannotCompileException(e); + } + + destInfo.setAccessFlags(destInfo.getAccessFlags() + & ~AccessFlag.ABSTRACT); + destClass.rebuildClassFile(); + } + + /** + * Obtains an attribute with the given name. + * If that attribute is not found in the class file, this + * method returns null. + * + *

Note that an attribute is a data block specified by + * the class file format. It is not an annotation. + * See {@link javassist.bytecode.AttributeInfo}. + * + * @param name attribute name + */ + public byte[] getAttribute(String name) { + AttributeInfo ai = methodInfo.getAttribute(name); + if (ai == null) + return null; + else + return ai.get(); + } + + /** + * Adds an attribute. The attribute is saved in the class file. + * + *

Note that an attribute is a data block specified by + * the class file format. It is not an annotation. + * See {@link javassist.bytecode.AttributeInfo}. + * + * @param name attribute name + * @param data attribute value + */ + public void setAttribute(String name, byte[] data) { + declaringClass.checkModify(); + methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(), + name, data)); + } + + /** + * Declares to use $cflow for this method/constructor. + * If $cflow is used, the class files modified + * with Javassist requires a support class + * javassist.runtime.Cflow at runtime + * (other Javassist classes are not required at runtime). + * + *

Every $cflow variable is given a unique name. + * For example, if the given name is "Point.paint", + * then the variable is indicated by $cflow(Point.paint). + * + * @param name $cflow name. It can include + * alphabets, numbers, _, + * $, and . (dot). + * + * @see javassist.runtime.Cflow + */ + public void useCflow(String name) throws CannotCompileException { + CtClass cc = declaringClass; + cc.checkModify(); + ClassPool pool = cc.getClassPool(); + String fname; + int i = 0; + while (true) { + fname = "_cflow$" + i++; + try { + cc.getDeclaredField(fname); + } + catch(NotFoundException e) { + break; + } + } + + pool.recordCflow(name, declaringClass.getName(), fname); + try { + CtClass type = pool.get("javassist.runtime.Cflow"); + CtField field = new CtField(type, fname, cc); + field.setModifiers(Modifier.PUBLIC | Modifier.STATIC); + cc.addField(field, CtField.Initializer.byNew(type)); + insertBefore(fname + ".enter();", false); + String src = fname + ".exit();"; + insertAfter(src, true); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + } + + /** + * Declares a new local variable. The scope of this variable is the + * whole method body. The initial value of that variable is not set. + * The declared variable can be accessed in the code snippet inserted + * by insertBefore(), insertAfter(), etc. + * + *

If the second parameter asFinally to + * insertAfter() is true, the declared local variable + * is not visible from the code inserted by insertAfter(). + * + * @param name the name of the variable + * @param type the type of the variable + * @see #insertBefore(String) + * @see #insertAfter(String) + */ + public void addLocalVariable(String name, CtClass type) + throws CannotCompileException + { + declaringClass.checkModify(); + ConstPool cp = methodInfo.getConstPool(); + CodeAttribute ca = methodInfo.getCodeAttribute(); + if (ca == null) + throw new CannotCompileException("no method body"); + + LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( + LocalVariableAttribute.tag); + if (va == null) { + va = new LocalVariableAttribute(cp); + ca.getAttributes().add(va); + } + + int maxLocals = ca.getMaxLocals(); + String desc = Descriptor.of(type); + va.addEntry(0, ca.getCodeLength(), + cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals); + ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc)); + } + + /** + * Inserts a new parameter, which becomes the first parameter. + */ + public void insertParameter(CtClass type) + throws CannotCompileException + { + declaringClass.checkModify(); + String desc = methodInfo.getDescriptor(); + String desc2 = Descriptor.insertParameter(type, desc); + try { + addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + + methodInfo.setDescriptor(desc2); + } + + /** + * Appends a new parameter, which becomes the last parameter. + */ + public void addParameter(CtClass type) + throws CannotCompileException + { + declaringClass.checkModify(); + String desc = methodInfo.getDescriptor(); + String desc2 = Descriptor.appendParameter(type, desc); + int offset = Modifier.isStatic(getModifiers()) ? 0 : 1; + try { + addParameter2(offset + Descriptor.paramSize(desc), type, desc); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + + methodInfo.setDescriptor(desc2); + } + + private void addParameter2(int where, CtClass type, String desc) + throws BadBytecode + { + CodeAttribute ca = methodInfo.getCodeAttribute(); + if (ca != null) { + int size = 1; + char typeDesc = 'L'; + int classInfo = 0; + if (type.isPrimitive()) { + CtPrimitiveType cpt = (CtPrimitiveType)type; + size = cpt.getDataSize(); + typeDesc = cpt.getDescriptor(); + } + else + classInfo = methodInfo.getConstPool().addClassInfo(type); + + ca.insertLocalVar(where, size); + LocalVariableAttribute va + = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag); + if (va != null) + va.shiftIndex(where, size); + + LocalVariableTypeAttribute lvta + = (LocalVariableTypeAttribute)ca.getAttribute(LocalVariableTypeAttribute.tag); + if (lvta != null) + lvta.shiftIndex(where, size); + + StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); + if (smt != null) + smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); + + StackMap sm = (StackMap)ca.getAttribute(StackMap.tag); + if (sm != null) + sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); + } + } + + /** + * Modifies the method/constructor body. + * + * @param converter specifies how to modify. + */ + public void instrument(CodeConverter converter) + throws CannotCompileException + { + declaringClass.checkModify(); + ConstPool cp = methodInfo.getConstPool(); + converter.doit(getDeclaringClass(), methodInfo, cp); + } + + /** + * Modifies the method/constructor body. + * + *

While executing this method, only replace() + * in Expr is available for bytecode modification. + * Other methods such as insertBefore() may collapse + * the bytecode because the ExprEditor loses + * its current position. + * + * @param editor specifies how to modify. + * @see javassist.expr.Expr#replace(String) + * @see #insertBefore(String) + */ + public void instrument(ExprEditor editor) + throws CannotCompileException + { + // if the class is not frozen, + // does not turn the modified flag on. + if (declaringClass.isFrozen()) + declaringClass.checkModify(); + + if (editor.doit(declaringClass, methodInfo)) + declaringClass.checkModify(); + } + + /** + * Inserts bytecode at the beginning of the body. + * + *

If this object represents a constructor, + * the bytecode is inserted before + * a constructor in the super class or this class is called. + * Therefore, the inserted bytecode is subject to constraints described + * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed). + * For example, it cannot access instance fields or methods although + * it may assign a value to an instance field directly declared in this + * class. Accessing static fields and methods is allowed. + * Use insertBeforeBody() in CtConstructor. + * + * @param src the source code representing the inserted bytecode. + * It must be a single statement or block. + * @see CtConstructor#insertBeforeBody(String) + */ + public void insertBefore(String src) throws CannotCompileException { + insertBefore(src, true); + } + + private void insertBefore(String src, boolean rebuild) + throws CannotCompileException + { + CtClass cc = declaringClass; + cc.checkModify(); + CodeAttribute ca = methodInfo.getCodeAttribute(); + if (ca == null) + throw new CannotCompileException("no method body"); + + CodeIterator iterator = ca.iterator(); + Javac jv = new Javac(cc); + try { + int nvars = jv.recordParams(getParameterTypes(), + Modifier.isStatic(getModifiers())); + jv.recordParamNames(ca, nvars); + jv.recordLocalVariables(ca, 0); + jv.recordType(getReturnType0()); + jv.compileStmnt(src); + Bytecode b = jv.getBytecode(); + int stack = b.getMaxStack(); + int locals = b.getMaxLocals(); + + if (stack > ca.getMaxStack()) + ca.setMaxStack(stack); + + if (locals > ca.getMaxLocals()) + ca.setMaxLocals(locals); + + int pos = iterator.insertEx(b.get()); + iterator.insert(b.getExceptionTable(), pos); + if (rebuild) + methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + /** + * Inserts bytecode at the end of the body. + * The bytecode is inserted just before every return insturction. + * It is not executed when an exception is thrown. + * + * @param src the source code representing the inserted bytecode. + * It must be a single statement or block. + */ + public void insertAfter(String src) + throws CannotCompileException + { + insertAfter(src, false); + } + + /** + * Inserts bytecode at the end of the body. + * The bytecode is inserted just before every return insturction. + * + * @param src the source code representing the inserted bytecode. + * It must be a single statement or block. + * @param asFinally true if the inserted bytecode is executed + * not only when the control normally returns + * but also when an exception is thrown. + * If this parameter is true, the inserted code cannot + * access local variables. + */ + public void insertAfter(String src, boolean asFinally) + throws CannotCompileException + { + CtClass cc = declaringClass; + cc.checkModify(); + ConstPool pool = methodInfo.getConstPool(); + CodeAttribute ca = methodInfo.getCodeAttribute(); + if (ca == null) + throw new CannotCompileException("no method body"); + + CodeIterator iterator = ca.iterator(); + int retAddr = ca.getMaxLocals(); + Bytecode b = new Bytecode(pool, 0, retAddr + 1); + b.setStackDepth(ca.getMaxStack() + 1); + Javac jv = new Javac(b, cc); + try { + int nvars = jv.recordParams(getParameterTypes(), + Modifier.isStatic(getModifiers())); + jv.recordParamNames(ca, nvars); + CtClass rtype = getReturnType0(); + int varNo = jv.recordReturnType(rtype, true); + jv.recordLocalVariables(ca, 0); + + // finally clause for exceptions + int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo, + jv, src); + int handlerPos = iterator.getCodeLength(); + if (asFinally) + ca.getExceptionTable().add(getStartPosOfBody(ca), handlerPos, handlerPos, 0); + + int adviceLen = 0; + int advicePos = 0; + boolean noReturn = true; + while (iterator.hasNext()) { + int pos = iterator.next(); + if (pos >= handlerPos) + break; + + int c = iterator.byteAt(pos); + if (c == Opcode.ARETURN || c == Opcode.IRETURN + || c == Opcode.FRETURN || c == Opcode.LRETURN + || c == Opcode.DRETURN || c == Opcode.RETURN) { + if (noReturn) { + // finally clause for normal termination + adviceLen = insertAfterAdvice(b, jv, src, pool, rtype, varNo); + handlerPos = iterator.append(b.get()); + iterator.append(b.getExceptionTable(), handlerPos); + advicePos = iterator.getCodeLength() - adviceLen; + handlerLen = advicePos - handlerPos; + noReturn = false; + } + insertGoto(iterator, advicePos, pos); + advicePos = iterator.getCodeLength() - adviceLen; + handlerPos = advicePos - handlerLen; + } + } + + if (noReturn) { + handlerPos = iterator.append(b.get()); + iterator.append(b.getExceptionTable(), handlerPos); + } + + ca.setMaxStack(b.getMaxStack()); + ca.setMaxLocals(b.getMaxLocals()); + methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + private int insertAfterAdvice(Bytecode code, Javac jv, String src, + ConstPool cp, CtClass rtype, int varNo) + throws CompileError + { + int pc = code.currentPc(); + if (rtype == CtClass.voidType) { + code.addOpcode(Opcode.ACONST_NULL); + code.addAstore(varNo); + jv.compileStmnt(src); + code.addOpcode(Opcode.RETURN); + if (code.getMaxLocals() < 1) + code.setMaxLocals(1); + } + else { + code.addStore(varNo, rtype); + jv.compileStmnt(src); + code.addLoad(varNo, rtype); + if (rtype.isPrimitive()) + code.addOpcode(((CtPrimitiveType)rtype).getReturnOp()); + else + code.addOpcode(Opcode.ARETURN); + } + + return code.currentPc() - pc; + } + + /* + * assert subr > pos + */ + private void insertGoto(CodeIterator iterator, int subr, int pos) + throws BadBytecode + { + iterator.setMark(subr); + // the gap length might be a multiple of 4. + iterator.writeByte(Opcode.NOP, pos); + boolean wide = subr + 2 - pos > Short.MAX_VALUE; + int len = wide ? 4 : 2; + CodeIterator.Gap gap = iterator.insertGapAt(pos, len, false); + pos = gap.position + gap.length - len; + int offset = iterator.getMark() - pos; + if (wide) { + iterator.writeByte(Opcode.GOTO_W, pos); + iterator.write32bit(offset, pos + 1); + } + else if (offset <= Short.MAX_VALUE) { + iterator.writeByte(Opcode.GOTO, pos); + iterator.write16bit(offset, pos + 1); + } + else { + if (gap.length < 4) { + CodeIterator.Gap gap2 = iterator.insertGapAt(gap.position, 2, false); + pos = gap2.position + gap2.length + gap.length - 4; + } + + iterator.writeByte(Opcode.GOTO_W, pos); + iterator.write32bit(iterator.getMark() - pos, pos + 1); + } + } + + /* insert a finally clause + */ + private int insertAfterHandler(boolean asFinally, Bytecode b, + CtClass rtype, int returnVarNo, + Javac javac, String src) + throws CompileError + { + if (!asFinally) + return 0; + + int var = b.getMaxLocals(); + b.incMaxLocals(1); + int pc = b.currentPc(); + b.addAstore(var); // store an exception + if (rtype.isPrimitive()) { + char c = ((CtPrimitiveType)rtype).getDescriptor(); + if (c == 'D') { + b.addDconst(0.0); + b.addDstore(returnVarNo); + } + else if (c == 'F') { + b.addFconst(0); + b.addFstore(returnVarNo); + } + else if (c == 'J') { + b.addLconst(0); + b.addLstore(returnVarNo); + } + else if (c == 'V') { + b.addOpcode(Opcode.ACONST_NULL); + b.addAstore(returnVarNo); + } + else { // int, boolean, char, short, ... + b.addIconst(0); + b.addIstore(returnVarNo); + } + } + else { + b.addOpcode(Opcode.ACONST_NULL); + b.addAstore(returnVarNo); + } + + javac.compileStmnt(src); + b.addAload(var); + b.addOpcode(Opcode.ATHROW); + return b.currentPc() - pc; + } + + /* -- OLD version -- + + public void insertAfter(String src) throws CannotCompileException { + declaringClass.checkModify(); + CodeAttribute ca = methodInfo.getCodeAttribute(); + CodeIterator iterator = ca.iterator(); + Bytecode b = new Bytecode(methodInfo.getConstPool(), + ca.getMaxStack(), ca.getMaxLocals()); + b.setStackDepth(ca.getMaxStack()); + Javac jv = new Javac(b, declaringClass); + try { + jv.recordParams(getParameterTypes(), + Modifier.isStatic(getModifiers())); + CtClass rtype = getReturnType0(); + int varNo = jv.recordReturnType(rtype, true); + boolean isVoid = rtype == CtClass.voidType; + if (isVoid) { + b.addOpcode(Opcode.ACONST_NULL); + b.addAstore(varNo); + jv.compileStmnt(src); + } + else { + b.addStore(varNo, rtype); + jv.compileStmnt(src); + b.addLoad(varNo, rtype); + } + + byte[] code = b.get(); + ca.setMaxStack(b.getMaxStack()); + ca.setMaxLocals(b.getMaxLocals()); + while (iterator.hasNext()) { + int pos = iterator.next(); + int c = iterator.byteAt(pos); + if (c == Opcode.ARETURN || c == Opcode.IRETURN + || c == Opcode.FRETURN || c == Opcode.LRETURN + || c == Opcode.DRETURN || c == Opcode.RETURN) + iterator.insert(pos, code); + } + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + */ + + /** + * Adds a catch clause that handles an exception thrown in the + * body. The catch clause must end with a return or throw statement. + * + * @param src the source code representing the catch clause. + * It must be a single statement or block. + * @param exceptionType the type of the exception handled by the + * catch clause. + */ + public void addCatch(String src, CtClass exceptionType) + throws CannotCompileException + { + addCatch(src, exceptionType, "$e"); + } + + /** + * Adds a catch clause that handles an exception thrown in the + * body. The catch clause must end with a return or throw statement. + * + * @param src the source code representing the catch clause. + * It must be a single statement or block. + * @param exceptionType the type of the exception handled by the + * catch clause. + * @param exceptionName the name of the variable containing the + * caught exception, for example, + * $e. + */ + public void addCatch(String src, CtClass exceptionType, + String exceptionName) + throws CannotCompileException + { + CtClass cc = declaringClass; + cc.checkModify(); + ConstPool cp = methodInfo.getConstPool(); + CodeAttribute ca = methodInfo.getCodeAttribute(); + CodeIterator iterator = ca.iterator(); + Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals()); + b.setStackDepth(1); + Javac jv = new Javac(b, cc); + try { + jv.recordParams(getParameterTypes(), + Modifier.isStatic(getModifiers())); + int var = jv.recordVariable(exceptionType, exceptionName); + b.addAstore(var); + jv.compileStmnt(src); + + int stack = b.getMaxStack(); + int locals = b.getMaxLocals(); + + if (stack > ca.getMaxStack()) + ca.setMaxStack(stack); + + if (locals > ca.getMaxLocals()) + ca.setMaxLocals(locals); + + int len = iterator.getCodeLength(); + int pos = iterator.append(b.get()); + ca.getExceptionTable().add(getStartPosOfBody(ca), len, len, + cp.addClassInfo(exceptionType)); + iterator.append(b.getExceptionTable(), pos); + methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + /* CtConstructor overrides this method. + */ + int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException { + return 0; + } + + /** + * Inserts bytecode at the specified line in the body. + * It is equivalent to: + * + *
insertAt(lineNum, true, src) + * + *
See this method as well. + * + * @param lineNum the line number. The bytecode is inserted at the + * beginning of the code at the line specified by this + * line number. + * @param src the source code representing the inserted bytecode. + * It must be a single statement or block. + * @return the line number at which the bytecode has been inserted. + * + * @see CtBehavior#insertAt(int,boolean,String) + */ + public int insertAt(int lineNum, String src) + throws CannotCompileException + { + return insertAt(lineNum, true, src); + } + + /** + * Inserts bytecode at the specified line in the body. + * + *

If there is not + * a statement at the specified line, the bytecode might be inserted + * at the line including the first statement after that line specified. + * For example, if there is only a closing brace at that line, the + * bytecode would be inserted at another line below. + * To know exactly where the bytecode will be inserted, call with + * modify set to false. + * + * @param lineNum the line number. The bytecode is inserted at the + * beginning of the code at the line specified by this + * line number. + * @param modify if false, this method does not insert the bytecode. + * It instead only returns the line number at which + * the bytecode would be inserted. + * @param src the source code representing the inserted bytecode. + * It must be a single statement or block. + * If modify is false, the value of src can be null. + * @return the line number at which the bytecode has been inserted. + */ + public int insertAt(int lineNum, boolean modify, String src) + throws CannotCompileException + { + CodeAttribute ca = methodInfo.getCodeAttribute(); + if (ca == null) + throw new CannotCompileException("no method body"); + + LineNumberAttribute ainfo + = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); + if (ainfo == null) + throw new CannotCompileException("no line number info"); + + LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum); + lineNum = pc.line; + int index = pc.index; + if (!modify) + return lineNum; + + CtClass cc = declaringClass; + cc.checkModify(); + CodeIterator iterator = ca.iterator(); + Javac jv = new Javac(cc); + try { + jv.recordLocalVariables(ca, index); + jv.recordParams(getParameterTypes(), + Modifier.isStatic(getModifiers())); + jv.setMaxLocals(ca.getMaxLocals()); + jv.compileStmnt(src); + Bytecode b = jv.getBytecode(); + int locals = b.getMaxLocals(); + int stack = b.getMaxStack(); + ca.setMaxLocals(locals); + + /* We assume that there is no values in the operand stack + * at the position where the bytecode is inserted. + */ + if (stack > ca.getMaxStack()) + ca.setMaxStack(stack); + + index = iterator.insertAt(index, b.get()); + iterator.insert(b.getExceptionTable(), index); + methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); + return lineNum; + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtClass.java b/src/main/java/com/wenshuo/agent/javassist/CtClass.java new file mode 100644 index 0000000..21fe5ac --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtClass.java @@ -0,0 +1,1595 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URL; +import java.security.ProtectionDomain; +import java.util.Collection; + +import com.wenshuo.agent.javassist.bytecode.ClassFile; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.bytecode.Opcode; +import com.wenshuo.agent.javassist.expr.ExprEditor; + +/* Note: + * + * This class is an abstract class and several methods just return null + * or throw an exception. Those methods are overridden in subclasses + * of this class. Read the source code of CtClassType if you are + * interested in the implementation of Javassist. + * + * Subclasses of CtClass are CtClassType, CtPrimitiveType, and CtArray. + */ + +/** + * An instance of CtClass represents a class. + * It is obtained from ClassPool. + * + * @see ClassPool#get(String) + */ +public abstract class CtClass { + protected String qualifiedName; + + /** + * If the value of this field is not null, then all class + * files modified by Javassist are saved under the directory + * specified by this variable. For example, if the value is + * "./debug", then all class files are saved + * there. The directory name must not end with a directory + * separator such as /. + * + *

The default value is null. + * + * @see #debugWriteFile(String) + * @since 3.16 + */ + public static String debugDump = null; + + /** + * The version number of this release. + */ + public static final String version = "3.20.0-GA"; + + /** + * Prints the version number and the copyright notice. + * + *

The following command invokes this method: + * + *

java -jar javassist.jar
+ */ + public static void main(String[] args) { + System.out.println("Javassist version " + CtClass.version); + System.out.println("Copyright (C) 1999-2015 Shigeru Chiba." + + " All Rights Reserved."); + } + + static final String javaLangObject = "java.lang.Object"; + + /** + * The CtClass object representing + * the boolean type. + */ + public static CtClass booleanType; + + /** + * The CtClass object representing + * the char type. + */ + public static CtClass charType; + + /** + * The CtClass object representing + * the byte type. + */ + public static CtClass byteType; + + /** + * The CtClass object representing + * the short type. + */ + public static CtClass shortType; + + /** + * The CtClass object representing + * the int type. + */ + public static CtClass intType; + + /** + * The CtClass object representing + * the long type. + */ + public static CtClass longType; + + /** + * The CtClass object representing + * the float type. + */ + public static CtClass floatType; + + /** + * The CtClass object representing + * the double type. + */ + public static CtClass doubleType; + + /** + * The CtClass object representing + * the void type. + */ + public static CtClass voidType; + + static CtClass[] primitiveTypes; + + static { + primitiveTypes = new CtClass[9]; + + booleanType = + new CtPrimitiveType("boolean", 'Z', "java.lang.Boolean", + "booleanValue", "()Z", Opcode.IRETURN, + Opcode.T_BOOLEAN, 1); + primitiveTypes[0] = booleanType; + + charType = new CtPrimitiveType("char", 'C', "java.lang.Character", + "charValue", "()C", Opcode.IRETURN, + Opcode.T_CHAR, 1); + primitiveTypes[1] = charType; + + byteType = new CtPrimitiveType("byte", 'B', "java.lang.Byte", + "byteValue", "()B", Opcode.IRETURN, + Opcode.T_BYTE, 1); + primitiveTypes[2] = byteType; + + shortType = new CtPrimitiveType("short", 'S', "java.lang.Short", + "shortValue", "()S", Opcode.IRETURN, + Opcode.T_SHORT, 1); + primitiveTypes[3] = shortType; + + intType = new CtPrimitiveType("int", 'I', "java.lang.Integer", + "intValue", "()I", Opcode.IRETURN, + Opcode.T_INT, 1); + primitiveTypes[4] = intType; + + longType = new CtPrimitiveType("long", 'J', "java.lang.Long", + "longValue", "()J", Opcode.LRETURN, + Opcode.T_LONG, 2); + primitiveTypes[5] = longType; + + floatType = new CtPrimitiveType("float", 'F', "java.lang.Float", + "floatValue", "()F", Opcode.FRETURN, + Opcode.T_FLOAT, 1); + primitiveTypes[6] = floatType; + + doubleType = new CtPrimitiveType("double", 'D', "java.lang.Double", + "doubleValue", "()D", Opcode.DRETURN, + Opcode.T_DOUBLE, 2); + primitiveTypes[7] = doubleType; + + voidType = new CtPrimitiveType("void", 'V', "java.lang.Void", + null, null, Opcode.RETURN, 0, 0); + primitiveTypes[8] = voidType; + } + + protected CtClass(String name) { + qualifiedName = name; + } + + /** + * Converts the object to a string. + */ + public String toString() { + StringBuffer buf = new StringBuffer(getClass().getName()); + buf.append("@"); + buf.append(Integer.toHexString(hashCode())); + buf.append("["); + extendToString(buf); + buf.append("]"); + return buf.toString(); + } + + /** + * Implemented in subclasses to add to the {@link #toString()} result. + * Subclasses should put a space before each token added to the buffer. + */ + protected void extendToString(StringBuffer buffer) { + buffer.append(getName()); + } + + /** + * Returns a ClassPool for this class. + */ + public ClassPool getClassPool() { return null; } + + /** + * Returns a class file for this class. + * + *

This method is not available if isFrozen() + * is true. + */ + public ClassFile getClassFile() { + checkModify(); + return getClassFile2(); + } + + /** + * Returns a class file for this class (read only). + * Normal applications do not need calling this method. Use + * getClassFile(). + * + *

The ClassFile object obtained by this method + * is read only. Changes to this object might not be reflected + * on a class file generated by toBytecode(), + * toClass(), etc. + * + *

This method is available even if isFrozen() + * is true. However, if the class is frozen, it might be also + * pruned. + * + * @see CtClass#getClassFile() + * @see CtClass#isFrozen() + * @see CtClass#prune() + */ + public ClassFile getClassFile2() { return null; } + + /** + * Undocumented method. Do not use; internal-use only. + */ + public com.wenshuo.agent.javassist.compiler.AccessorMaker getAccessorMaker() { + return null; + } + + /** + * Returns the uniform resource locator (URL) of the class file. + */ + public URL getURL() throws NotFoundException { + throw new NotFoundException(getName()); + } + + /** + * Returns true if the definition of the class has been modified. + */ + public boolean isModified() { return false; } + + /** + * Returns true if the class has been loaded or written out + * and thus it cannot be modified any more. + * + * @see #defrost() + * @see #detach() + */ + public boolean isFrozen() { return true; } + + /** + * Makes the class frozen. + * + * @see #isFrozen() + * @see #defrost() + * @since 3.6 + */ + public void freeze() {} + + /* Note: this method is overridden by CtClassType + */ + void checkModify() throws RuntimeException { + if (isFrozen()) + throw new RuntimeException(getName() + " class is frozen"); + + // isModified() must return true after this method is invoked. + } + + /** + * Defrosts the class so that the class can be modified again. + * + *

To avoid changes that will be never reflected, + * the class is frozen to be unmodifiable if it is loaded or + * written out. This method should be called only in a case + * that the class will be reloaded or written out later again. + * + *

If defrost() will be called later, pruning + * must be disallowed in advance. + * + * @see #isFrozen() + * @see #stopPruning(boolean) + * @see #detach() + */ + public void defrost() { + throw new RuntimeException("cannot defrost " + getName()); + } + + /** + * Returns true if this object represents a primitive + * Java type: boolean, byte, char, short, int, long, float, double, + * or void. + */ + public boolean isPrimitive() { return false; } + + /** + * Returns true if this object represents an array type. + */ + public boolean isArray() { + return false; + } + + /** + * If this object represents an array, this method returns the component + * type of the array. Otherwise, it returns null. + */ + public CtClass getComponentType() throws NotFoundException { + return null; + } + + /** + * Returns true if this class extends or implements + * clazz. It also returns true if + * this class is the same as clazz. + */ + public boolean subtypeOf(CtClass clazz) throws NotFoundException { + return this == clazz || getName().equals(clazz.getName()); + } + + /** + * Obtains the fully-qualified name of the class. + */ + public String getName() { return qualifiedName; } + + /** + * Obtains the not-qualified class name. + */ + public final String getSimpleName() { + String qname = qualifiedName; + int index = qname.lastIndexOf('.'); + if (index < 0) + return qname; + else + return qname.substring(index + 1); + } + + /** + * Obtains the package name. It may be null. + */ + public final String getPackageName() { + String qname = qualifiedName; + int index = qname.lastIndexOf('.'); + if (index < 0) + return null; + else + return qname.substring(0, index); + } + + /** + * Sets the class name + * + * @param name fully-qualified name + */ + public void setName(String name) { + checkModify(); + if (name != null) + qualifiedName = name; + } + + /** + * Returns the generic signature of the class. + * + *

The generics of Java is implemented by the erasure technique. + * After compilation, all type parameters are dropped off from the + * main part of a class file. However, for reflection, the type + * parameters are encoded into generic signatures and attached + * to a class file. + * + * @return null if the generic signature is not included. + * @see javassist.bytecode.SignatureAttribute#toClassSignature(String) + * @see CtMember#getGenericSignature() + * @since 3.17 + */ + public String getGenericSignature() { return null; } + + /** + * Sets the generic signature of the class. + * + *

The generics of Java is implemented by the erasure technique. + * After compilation, all type parameters are dropped off from the + * main part of a class file. However, for reflection, the type + * parameters must be encoded into generic signatures and attached + * to a class file. + * + *

For example, + * + *

class List<T> {
+     *     T value;
+     *     T get() { return value; }
+     *     void set(T v) { value = v; }
+     * }
+     * 
+ * + *

this class is generated by the following code: + * + *

+     * ClassPool pool = ClassPool.getDefault();
+     * CtClass cc = pool.makeClass("List");
+     * CtClass objectClass = pool.get(CtClass.javaLangObject);
+     * ClassSignature cs = new ClassSignature(
+     *                         new TypeParameter[] { new TypeParameter("T") });
+     * cc.setGenericSignature(cs.encode());    // <T:Ljava/lang/Object;>Ljava/lang/Object;
+     *
+     * CtField f = new CtField(objClass, "value", cc);
+     * TypeVariable tvar = new TypeVariable("T");
+     * f.setGenericSignature(tvar.encode());   // TT;
+     * cc.addField(f);
+     *
+     * CtMethod m = CtNewMethod.make("public Object get(){return value;}", cc);
+     * MethodSignature ms = new MethodSignature(null, null, tvar, null);
+     * m.setGenericSignature(ms.encode());     // ()TT;
+     * cc.addMethod(m);
+     *
+     * CtMethod m2 = CtNewMethod.make("public void set(Object v){value = v;}", cc);
+     * MethodSignature ms2 = new MethodSignature(null, new Type[] { tvar },
+     *                                           new BaseType("void"), null);
+     * m2.setGenericSignature(ms2.encode());   // (TT;)V;
+     * cc.addMethod(m2);
+     *
+     * cc.writeFile();
+     * 
+ * + *

The generated class file is equivalent to the following: + * + *

class List {
+     *     Object value;
+     *     Object get() { return value; }
+     *     void set(Object v) { value = v; }
+     * }
+ * + *

but it includes generic signatures for the class, the field, + * and the methods so that the type variable T can be + * accessible through reflection. + * + *

MethodSignature is a utility class. You can directly + * pass the signature string to the setGenericSignature method. + * For the specification of the signatures, see Section 4.7.9.1 Signatures + * of The Java Virtual Machine Specification (Java SE 8). + * + * @param sig a generic signature. + * @see javassist.bytecode.SignatureAttribute.ClassSignature#encode() + * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode() + * @see CtMember#setGenericSignature(String) + * @since 3.17 + */ + public void setGenericSignature(String sig) { checkModify(); } + + /** + * Substitutes newName for all occurrences of a class + * name oldName in the class file. + * + * @param oldName replaced class name + * @param newName substituted class name + */ + public void replaceClassName(String oldName, String newName) { + checkModify(); + } + + /** + * Changes class names appearing in the class file according to the + * given map. + * + *

All the class names appearing in the class file are tested + * with map to determine whether each class name is + * replaced or not. Thus this method can be used for collecting + * all the class names in the class file. To do that, first define + * a subclass of ClassMap so that get() + * records all the given parameters. Then, make an instance of + * that subclass as an empty hash-table. Finally, pass that instance + * to this method. After this method finishes, that instance would + * contain all the class names appearing in the class file. + * + * @param map the hashtable associating replaced class names + * with substituted names. + */ + public void replaceClassName(ClassMap map) { + checkModify(); + } + + /** + * Returns a collection of the names of all the classes + * referenced in this class. + * That collection includes the name of this class. + * + *

This method may return null. + * + * @return a Collection<String> object. + */ + public synchronized Collection getRefClasses() { + ClassFile cf = getClassFile2(); + if (cf != null) { + ClassMap cm = new ClassMap() { + public void put(String oldname, String newname) { + put0(oldname, newname); + } + + public Object get(Object jvmClassName) { + String n = toJavaName((String)jvmClassName); + put0(n, n); + return null; + } + + public void fix(String name) {} + }; + cf.getRefClasses(cm); + return cm.values(); + } + else + return null; + } + + /** + * Determines whether this object represents a class or an interface. + * It returns true if this object represents an interface. + */ + public boolean isInterface() { + return false; + } + + /** + * Determines whether this object represents an annotation type. + * It returns true if this object represents an annotation type. + * + * @since 3.2 + */ + public boolean isAnnotation() { + return false; + } + + /** + * Determines whether this object represents an enum. + * It returns true if this object represents an enum. + * + * @since 3.2 + */ + public boolean isEnum() { + return false; + } + + /** + * Returns the modifiers for this class, encoded in an integer. + * For decoding, use javassist.Modifier. + * + *

If the class is a static nested class (a.k.a. static inner class), + * the returned modifiers include Modifier.STATIC. + * + * @see Modifier + */ + public int getModifiers() { + return 0; + } + + /** + * Returns true if the class has the specified annotation class. + * + * @param clz the annotation class. + * @return true if the annotation is found, otherwise false. + * @since 3.11 + */ + public boolean hasAnnotation(Class clz) { + return false; + } + + /** + * Returns the annotation if the class has the specified annotation class. + * For example, if an annotation @Author is associated + * with this class, an Author object is returned. + * The member values can be obtained by calling methods on + * the Author object. + * + * @param clz the annotation class. + * @return the annotation if found, otherwise null. + * @since 3.11 + */ + public Object getAnnotation(Class clz) throws ClassNotFoundException { + return null; + } + + /** + * Returns the annotations associated with this class. + * For example, if an annotation @Author is associated + * with this class, the returned array contains an Author + * object. The member values can be obtained by calling methods on + * the Author object. + * + * @return an array of annotation-type objects. + * @see CtMember#getAnnotations() + * @since 3.1 + */ + public Object[] getAnnotations() throws ClassNotFoundException { + return new Object[0]; + } + + /** + * Returns the annotations associated with this class. + * This method is equivalent to getAnnotations() + * except that, if any annotations are not on the classpath, + * they are not included in the returned array. + * + * @return an array of annotation-type objects. + * @see #getAnnotations() + * @see CtMember#getAvailableAnnotations() + * @since 3.3 + */ + public Object[] getAvailableAnnotations(){ + return new Object[0]; + } + + /** + * Returns an array of nested classes declared in the class. + * Nested classes are inner classes, anonymous classes, local classes, + * and static nested classes. This simply calls getNestedClasses(). + * + * @see #getNestedClasses() + * @since 3.15 + */ + public CtClass[] getDeclaredClasses() throws NotFoundException { + return getNestedClasses(); + } + + /** + * Returns an array of nested classes declared in the class. + * Nested classes are inner classes, anonymous classes, local classes, + * and static nested classes. + * + * @since 3.2 + */ + public CtClass[] getNestedClasses() throws NotFoundException { + return new CtClass[0]; + } + + /** + * Sets the modifiers. + * + *

If the class is a nested class, this method also modifies + * the class declaring that nested class (i.e. the enclosing + * class is modified). + * + * @param mod modifiers encoded by + * javassist.Modifier + * @see Modifier + */ + public void setModifiers(int mod) { + checkModify(); + } + + /** + * Determines whether the class directly or indirectly extends + * the given class. If this class extends a class A and + * the class A extends a class B, then subclassof(B) returns true. + * + *

This method returns true if the given class is identical to + * the class represented by this object. + */ + public boolean subclassOf(CtClass superclass) { + return false; + } + + /** + * Obtains the class object representing the superclass of the + * class. + * It returns null if this object represents the + * java.lang.Object class and thus it does not have + * the super class. + * + *

If this object represents an interface, this method + * always returns the java.lang.Object class. + * To obtain the super interfaces + * extended by that interface, call getInterfaces(). + */ + public CtClass getSuperclass() throws NotFoundException { + return null; + } + + /** + * Changes a super class unless this object represents an interface. + * The new super class must be compatible with the old one; for example, + * it should inherit from the old super class. + * + *

If this object represents an interface, this method is equivalent + * to addInterface(); it appends clazz to + * the list of the super interfaces extended by that interface. + * Note that an interface can extend multiple super interfaces. + * + * @see #replaceClassName(String, String) + * @see #replaceClassName(ClassMap) + */ + public void setSuperclass(CtClass clazz) throws CannotCompileException { + checkModify(); + } + + /** + * Obtains the class objects representing the interfaces implemented + * by the class or, if this object represents an interface, the interfaces + * extended by that interface. + */ + public CtClass[] getInterfaces() throws NotFoundException { + return new CtClass[0]; + } + + /** + * Sets implemented interfaces. If this object represents an interface, + * this method sets the interfaces extended by that interface. + * + * @param list a list of the CtClass objects + * representing interfaces, or + * null if the class implements + * no interfaces. + */ + public void setInterfaces(CtClass[] list) { + checkModify(); + } + + /** + * Adds an interface. + * + * @param anInterface the added interface. + */ + public void addInterface(CtClass anInterface) { + checkModify(); + } + + /** + * If this class is a member class or interface of another class, + * then the class enclosing this class is returned. + * + * @return null if this class is a top-level class or an anonymous class. + */ + public CtClass getDeclaringClass() throws NotFoundException { + return null; + } + + /** + * Returns the immediately enclosing method of this class. + * This method works only with JDK 1.5 or later. + * + * @return null if this class is not a local class or an anonymous + * class. + * @deprecated The enclosing method might be a constructor. + * Use {@link #getEnclosingBehavior()}. + * @see #getEnclosingBehavior() + */ + public final CtMethod getEnclosingMethod() throws NotFoundException { + CtBehavior b = getEnclosingBehavior(); + if (b == null) + return null; + else if (b instanceof CtMethod) + return (CtMethod)b; + else + throw new NotFoundException(b.getLongName() + " is enclosing " + getName()); + } + + /** + * Returns the immediately enclosing method of this class. + * It might be not a method but a constructor. + * This method works only with JDK 1.5 or later. + * + * @return null if this class is not a local class or an anonymous + * class. + */ + public CtBehavior getEnclosingBehavior() throws NotFoundException { + return null; + } + + /** + * Makes a new public nested class. If this method is called, + * the CtClass, which encloses the nested class, is modified + * since a class file includes a list of nested classes. + * + *

The current implementation only supports a static nested class. + * isStatic must be true. + * + * @param name the simple name of the nested class. + * @param isStatic true if the nested class is static. + */ + public CtClass makeNestedClass(String name, boolean isStatic) { + throw new RuntimeException(getName() + " is not a class"); + } + + /** + * Returns an array containing CtField objects + * representing all the non-private fields of the class. + * That array includes non-private fields inherited from the + * superclasses. + */ + public CtField[] getFields() { return new CtField[0]; } + + /** + * Returns the field with the specified name. The returned field + * may be a private field declared in a super class or interface. + */ + public CtField getField(String name) throws NotFoundException { + return getField(name, null); + } + + /** + * Returns the field with the specified name and type. The returned field + * may be a private field declared in a super class or interface. + * Unlike Java, the JVM allows a class to have + * multiple fields with the same name but different types. + * + * @param name the field name. + * @param desc the type descriptor of the field. It is available by + * {@link CtField#getSignature()}. + * @see CtField#getSignature() + */ + public CtField getField(String name, String desc) throws NotFoundException { + throw new NotFoundException(name); + } + + /** + * @return null if the specified field is not found. + */ + CtField getField2(String name, String desc) { return null; } + + /** + * Gets all the fields declared in the class. The inherited fields + * are not included. + * + *

Note: the result does not include inherited fields. + */ + public CtField[] getDeclaredFields() { return new CtField[0]; } + + /** + * Retrieves the field with the specified name among the fields + * declared in the class. + * + *

Note: this method does not search the super classes. + */ + public CtField getDeclaredField(String name) throws NotFoundException { + throw new NotFoundException(name); + } + + /** + * Retrieves the field with the specified name and type among the fields + * declared in the class. Unlike Java, the JVM allows a class to have + * multiple fields with the same name but different types. + * + *

Note: this method does not search the super classes. + * + * @param name the field name. + * @param desc the type descriptor of the field. It is available by + * {@link CtField#getSignature()}. + * @see CtField#getSignature() + */ + public CtField getDeclaredField(String name, String desc) throws NotFoundException { + throw new NotFoundException(name); + } + + /** + * Gets all the constructors and methods declared in the class. + */ + public CtBehavior[] getDeclaredBehaviors() { + return new CtBehavior[0]; + } + + /** + * Returns an array containing CtConstructor objects + * representing all the non-private constructors of the class. + */ + public CtConstructor[] getConstructors() { + return new CtConstructor[0]; + } + + /** + * Returns the constructor with the given signature, + * which is represented by a character string + * called method descriptor. + * For details of the method descriptor, see the JVM specification + * or javassist.bytecode.Descriptor. + * + * @param desc method descriptor + * @see javassist.bytecode.Descriptor + */ + public CtConstructor getConstructor(String desc) + throws NotFoundException + { + throw new NotFoundException("no such constructor"); + } + + /** + * Gets all the constructors declared in the class. + * + * @see javassist.CtConstructor + */ + public CtConstructor[] getDeclaredConstructors() { + return new CtConstructor[0]; + } + + /** + * Returns a constructor receiving the specified parameters. + * + * @param params parameter types. + */ + public CtConstructor getDeclaredConstructor(CtClass[] params) + throws NotFoundException + { + String desc = Descriptor.ofConstructor(params); + return getConstructor(desc); + } + + /** + * Gets the class initializer (static constructor) + * declared in the class. + * This method returns null if + * no class initializer is not declared. + * + * @see #makeClassInitializer() + * @see javassist.CtConstructor + */ + public CtConstructor getClassInitializer() { + return null; + } + + /** + * Returns an array containing CtMethod objects + * representing all the non-private methods of the class. + * That array includes non-private methods inherited from the + * superclasses. + */ + public CtMethod[] getMethods() { + return new CtMethod[0]; + } + + /** + * Returns the method with the given name and signature. + * The returned method may be declared in a super class. + * The method signature is represented by a character string + * called method descriptor, + * which is defined in the JVM specification. + * + * @param name method name + * @param desc method descriptor + * @see CtBehavior#getSignature() + * @see javassist.bytecode.Descriptor + */ + public CtMethod getMethod(String name, String desc) + throws NotFoundException + { + throw new NotFoundException(name); + } + + /** + * Gets all methods declared in the class. The inherited methods + * are not included. + * + * @see javassist.CtMethod + */ + public CtMethod[] getDeclaredMethods() { + return new CtMethod[0]; + } + + /** + * Retrieves the method with the specified name and parameter types + * among the methods declared in the class. + * + *

Note: this method does not search the superclasses. + * + * @param name method name + * @param params parameter types + * @see javassist.CtMethod + */ + public CtMethod getDeclaredMethod(String name, CtClass[] params) + throws NotFoundException + { + throw new NotFoundException(name); + } + + /** + * Retrieves methods with the specified name among the methods + * declared in the class. Multiple methods with different parameters + * may be returned. + * + *

Note: this method does not search the superclasses.

+ * + * @param name method name. + * @since 3.19 + */ + public CtMethod[] getDeclaredMethods(String name) throws NotFoundException { + throw new NotFoundException(name); + } + + /** + * Retrieves the method with the specified name among the methods + * declared in the class. If there are multiple methods with + * the specified name, then this method returns one of them. + * + *

Note: this method does not search the superclasses. + * + * @see javassist.CtMethod + */ + public CtMethod getDeclaredMethod(String name) throws NotFoundException { + throw new NotFoundException(name); + } + + /** + * Makes an empty class initializer (static constructor). + * If the class already includes a class initializer, + * this method returns it. + * + * @see #getClassInitializer() + */ + public CtConstructor makeClassInitializer() + throws CannotCompileException + { + throw new CannotCompileException("not a class"); + } + + /** + * Adds a constructor. To add a class initializer (static constructor), + * call makeClassInitializer(). + * + * @see #makeClassInitializer() + */ + public void addConstructor(CtConstructor c) + throws CannotCompileException + { + checkModify(); + } + + /** + * Removes a constructor declared in this class. + * + * @param c removed constructor. + * @throws NotFoundException if the constructor is not found. + */ + public void removeConstructor(CtConstructor c) throws NotFoundException { + checkModify(); + } + + /** + * Adds a method. + */ + public void addMethod(CtMethod m) throws CannotCompileException { + checkModify(); + } + + /** + * Removes a method declared in this class. + * + * @param m removed method. + * @throws NotFoundException if the method is not found. + */ + public void removeMethod(CtMethod m) throws NotFoundException { + checkModify(); + } + + /** + * Adds a field. + * + *

The CtField belonging to another + * CtClass cannot be directly added to this class. + * Only a field created for this class can be added. + * + * @see javassist.CtField#CtField(CtField,CtClass) + */ + public void addField(CtField f) throws CannotCompileException { + addField(f, (CtField.Initializer)null); + } + + /** + * Adds a field with an initial value. + * + *

The CtField belonging to another + * CtClass cannot be directly added to this class. + * Only a field created for this class can be added. + * + *

The initial value is given as an expression written in Java. + * Any regular Java expression can be used for specifying the initial + * value. The followings are examples. + * + *

+     * cc.addField(f, "0")               // the initial value is 0.
+     * cc.addField(f, "i + 1")           // i + 1.
+     * cc.addField(f, "new Point()");    // a Point object.
+     * 
+ * + *

Here, the type of variable cc is CtClass. + * The type of f is CtField. + * + *

Note: do not change the modifier of the field + * (in particular, do not add or remove static + * to/from the modifier) + * after it is added to the class by addField(). + * + * @param init an expression for the initial value. + * + * @see javassist.CtField.Initializer#byExpr(String) + * @see javassist.CtField#CtField(CtField,CtClass) + */ + public void addField(CtField f, String init) + throws CannotCompileException + { + checkModify(); + } + + /** + * Adds a field with an initial value. + * + *

The CtField belonging to another + * CtClass cannot be directly added to this class. + * Only a field created for this class can be added. + * + *

For example, + * + *

+     * CtClass cc = ...;
+     * addField(new CtField(CtClass.intType, "i", cc),
+     *          CtField.Initializer.constant(1));
+     * 
+ * + *

This code adds an int field named "i". The + * initial value of this field is 1. + * + * @param init specifies the initial value of the field. + * + * @see javassist.CtField#CtField(CtField,CtClass) + */ + public void addField(CtField f, CtField.Initializer init) + throws CannotCompileException + { + checkModify(); + } + + /** + * Removes a field declared in this class. + * + * @param f removed field. + * @throws NotFoundException if the field is not found. + */ + public void removeField(CtField f) throws NotFoundException { + checkModify(); + } + + /** + * Obtains an attribute with the given name. + * If that attribute is not found in the class file, this + * method returns null. + * + *

This is a convenient method mainly for obtaining + * a user-defined attribute. For dealing with attributes, see the + * javassist.bytecode package. For example, the following + * expression returns all the attributes of a class file. + * + *

+     * getClassFile().getAttributes()
+     * 
+ * + * @param name attribute name + * @see javassist.bytecode.AttributeInfo + */ + public byte[] getAttribute(String name) { + return null; + } + + /** + * Adds a named attribute. + * An arbitrary data (smaller than 64Kb) can be saved in the class + * file. Some attribute name are reserved by the JVM. + * The attributes with the non-reserved names are ignored when a + * class file is loaded into the JVM. + * If there is already an attribute with + * the same name, this method substitutes the new one for it. + * + *

This is a convenient method mainly for adding + * a user-defined attribute. For dealing with attributes, see the + * javassist.bytecode package. For example, the following + * expression adds an attribute info to a class file. + * + *

+     * getClassFile().addAttribute(info)
+     * 
+ * + * @param name attribute name + * @param data attribute value + * @see javassist.bytecode.AttributeInfo + */ + public void setAttribute(String name, byte[] data) { + checkModify(); + } + + /** + * Applies the given converter to all methods and constructors + * declared in the class. This method calls instrument() + * on every CtMethod and CtConstructor object + * in the class. + * + * @param converter specifies how to modify. + */ + public void instrument(CodeConverter converter) + throws CannotCompileException + { + checkModify(); + } + + /** + * Modifies the bodies of all methods and constructors + * declared in the class. This method calls instrument() + * on every CtMethod and CtConstructor object + * in the class. + * + * @param editor specifies how to modify. + */ + public void instrument(ExprEditor editor) + throws CannotCompileException + { + checkModify(); + } + + /** + * Converts this class to a java.lang.Class object. + * Once this method is called, further modifications are not + * allowed any more. + * To load the class, this method uses the context class loader + * of the current thread. If the program is running on some application + * server, the context class loader might be inappropriate to load the + * class. + * + *

This method is provided for convenience. If you need more + * complex functionality, you should write your own class loader. + * + *

Note: this method calls toClass() + * in ClassPool. + * + *

Warining: A Class object returned by this method may not + * work with a security manager or a signed jar file because a + * protection domain is not specified. + * + * @see #toClass(java.lang.ClassLoader,ProtectionDomain) + * @see ClassPool#toClass(CtClass) + */ + public Class toClass() throws CannotCompileException { + return getClassPool().toClass(this); + } + + /** + * Converts this class to a java.lang.Class object. + * Once this method is called, further modifications are not allowed + * any more. + * + *

The class file represented by this CtClass is + * loaded by the given class loader to construct a + * java.lang.Class object. Since a private method + * on the class loader is invoked through the reflection API, + * the caller must have permissions to do that. + * + *

An easy way to obtain ProtectionDomain object is + * to call getProtectionDomain() + * in java.lang.Class. It returns the domain that + * the class belongs to. + * + *

This method is provided for convenience. If you need more + * complex functionality, you should write your own class loader. + * + *

Note: this method calls toClass() + * in ClassPool. + * + * @param loader the class loader used to load this class. + * If it is null, the class loader returned by + * {@link ClassPool#getClassLoader()} is used. + * @param domain the protection domain that the class belongs to. + * If it is null, the default domain created + * by java.lang.ClassLoader is used. + * @see ClassPool#toClass(CtClass,java.lang.ClassLoader) + * @since 3.3 + */ + public Class toClass(ClassLoader loader, ProtectionDomain domain) + throws CannotCompileException + { + ClassPool cp = getClassPool(); + if (loader == null) + loader = cp.getClassLoader(); + + return cp.toClass(this, loader, domain); + } + + /** + * Converts this class to a java.lang.Class object. + * + *

Warining: A Class object returned by this method may not + * work with a security manager or a signed jar file because a + * protection domain is not specified. + * + * @deprecated Replaced by {@link #toClass(ClassLoader,ProtectionDomain)} + */ + public final Class toClass(ClassLoader loader) + throws CannotCompileException + { + return getClassPool().toClass(this, loader); + } + + /** + * Removes this CtClass object from the + * ClassPool. + * After this method is called, any method cannot be called on the + * removed CtClass object. + * + *

If get() in ClassPool is called + * with the name of the removed method, + * the ClassPool will read the class file again + * and constructs another CtClass object representing + * the same class. + */ + public void detach() { + ClassPool cp = getClassPool(); + CtClass obj = cp.removeCached(getName()); + if (obj != this) + cp.cacheCtClass(getName(), obj, false); + } + + /** + * Disallows (or allows) automatically pruning this CtClass + * object. + * + *

+ * Javassist can automatically prune a CtClass object + * when toBytecode() (or toClass(), + * writeFile()) is called. + * Since a ClassPool holds all instances of CtClass + * even after toBytecode() (or toClass(), + * writeFile()) is called, pruning may significantly + * save memory consumption. + * + *

If ClassPool.doPruning is true, the automatic pruning + * is on by default. Otherwise, it is off. The default value of + * ClassPool.doPruning is false. + * + * @param stop disallow pruning if true. Otherwise, allow. + * @return the previous status of pruning. true if pruning is already stopped. + * + * @see #detach() + * @see #prune() + * @see ClassPool#doPruning + */ + public boolean stopPruning(boolean stop) { return true; } + + /** + * Discards unnecessary attributes, in particular, + * CodeAttributes (method bodies) of the class, + * to minimize the memory footprint. + * After calling this method, the class is read only. + * It cannot be modified any more. + * Furthermore, toBytecode(), + * writeFile(), toClass(), + * or instrument() cannot be called. + * However, the method names and signatures in the class etc. + * are still accessible. + * + *

toBytecode(), writeFile(), and + * toClass() internally call this method if + * automatic pruning is on. + * + *

According to some experiments, pruning does not really reduce + * memory consumption. Only about 20%. Since pruning takes time, + * it might not pay off. So the automatic pruning is off by default. + * + * @see #stopPruning(boolean) + * @see #detach() + * @see ClassPool#doPruning + * + * @see #toBytecode() + * @see #toClass() + * @see #writeFile() + * @see #instrument(CodeConverter) + * @see #instrument(ExprEditor) + */ + public void prune() {} + + /* Called by get() in ClassPool. + * CtClassType overrides this method. + */ + void incGetCounter() {} + + /** + * If this method is called, the class file will be + * rebuilt when it is finally generated by + * toBytecode() and writeFile(). + * For a performance reason, the symbol table of the + * class file may contain unused entries, for example, + * after a method or a filed is deleted. + * This method + * removes those unused entries. This removal will + * minimize the size of the class file. + * + * @since 3.8.1 + */ + public void rebuildClassFile() {} + + /** + * Converts this class to a class file. + * Once this method is called, further modifications are not + * possible any more. + * + * @return the contents of the class file. + */ + public byte[] toBytecode() throws IOException, CannotCompileException { + ByteArrayOutputStream barray = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(barray); + try { + toBytecode(out); + } + finally { + out.close(); + } + + return barray.toByteArray(); + } + + /** + * Writes a class file represented by this CtClass + * object in the current directory. + * Once this method is called, further modifications are not + * possible any more. + * + * @see #debugWriteFile() + */ + public void writeFile() + throws NotFoundException, IOException, CannotCompileException + { + writeFile("."); + } + + /** + * Writes a class file represented by this CtClass + * object on a local disk. + * Once this method is called, further modifications are not + * possible any more. + * + * @param directoryName it must end without a directory separator. + * @see #debugWriteFile(String) + */ + public void writeFile(String directoryName) + throws CannotCompileException, IOException + { + DataOutputStream out = makeFileOutput(directoryName); + try { + toBytecode(out); + } + finally { + out.close(); + } + } + + protected DataOutputStream makeFileOutput(String directoryName) { + String classname = getName(); + String filename = directoryName + File.separatorChar + + classname.replace('.', File.separatorChar) + ".class"; + int pos = filename.lastIndexOf(File.separatorChar); + if (pos > 0) { + String dir = filename.substring(0, pos); + if (!dir.equals(".")) + new File(dir).mkdirs(); + } + + return new DataOutputStream(new BufferedOutputStream( + new DelayedFileOutputStream(filename))); + } + + /** + * Writes a class file as writeFile() does although this + * method does not prune or freeze the class after writing the class + * file. Note that, once writeFile() or toBytecode() + * is called, it cannot be called again since the class is pruned and frozen. + * This method would be useful for debugging. + */ + public void debugWriteFile() { + debugWriteFile("."); + } + + /** + * Writes a class file as writeFile() does although this + * method does not prune or freeze the class after writing the class + * file. Note that, once writeFile() or toBytecode() + * is called, it cannot be called again since the class is pruned and frozen. + * This method would be useful for debugging. + * + * @param directoryName it must end without a directory separator. + */ + public void debugWriteFile(String directoryName) { + try { + boolean p = stopPruning(true); + writeFile(directoryName); + defrost(); + stopPruning(p); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + static class DelayedFileOutputStream extends OutputStream { + private FileOutputStream file; + private String filename; + + DelayedFileOutputStream(String name) { + file = null; + filename = name; + } + + private void init() throws IOException { + if (file == null) + file = new FileOutputStream(filename); + } + + public void write(int b) throws IOException { + init(); + file.write(b); + } + + public void write(byte[] b) throws IOException { + init(); + file.write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + init(); + file.write(b, off, len); + + } + + public void flush() throws IOException { + init(); + file.flush(); + } + + public void close() throws IOException { + init(); + file.close(); + } + } + + /** + * Converts this class to a class file. + * Once this method is called, further modifications are not + * possible any more. + * + *

This method dose not close the output stream in the end. + * + * @param out the output stream that a class file is written to. + */ + public void toBytecode(DataOutputStream out) + throws CannotCompileException, IOException + { + throw new CannotCompileException("not a class"); + } + + /** + * Makes a unique member name. This method guarantees that + * the returned name is not used as a prefix of any methods + * or fields visible in this class. + * If the returned name is XYZ, then any method or field names + * in this class do not start with XYZ. + * + * @param prefix the prefix of the member name. + */ + public String makeUniqueName(String prefix) { + throw new RuntimeException("not available in " + getName()); + } + + /* Invoked from ClassPool#compress(). + * This method is overridden by CtClassType. + */ + void compress() {} +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtClassType.java b/src/main/java/com/wenshuo/agent/javassist/CtClassType.java new file mode 100644 index 0000000..e7774e5 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtClassType.java @@ -0,0 +1,1761 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.lang.ref.WeakReference; +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Set; + +import com.wenshuo.agent.javassist.bytecode.AccessFlag; +import com.wenshuo.agent.javassist.bytecode.AttributeInfo; +import com.wenshuo.agent.javassist.bytecode.AnnotationsAttribute; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.Bytecode; +import com.wenshuo.agent.javassist.bytecode.ClassFile; +import com.wenshuo.agent.javassist.bytecode.CodeAttribute; +import com.wenshuo.agent.javassist.bytecode.ConstantAttribute; +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.bytecode.EnclosingMethodAttribute; +import com.wenshuo.agent.javassist.bytecode.FieldInfo; +import com.wenshuo.agent.javassist.bytecode.InnerClassesAttribute; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; +import com.wenshuo.agent.javassist.bytecode.ParameterAnnotationsAttribute; +import com.wenshuo.agent.javassist.bytecode.SignatureAttribute; +import com.wenshuo.agent.javassist.bytecode.annotation.Annotation; +import com.wenshuo.agent.javassist.compiler.AccessorMaker; +import com.wenshuo.agent.javassist.compiler.CompileError; +import com.wenshuo.agent.javassist.compiler.Javac; +import com.wenshuo.agent.javassist.expr.ExprEditor; + +/** + * Class types. + */ +class CtClassType extends CtClass { + ClassPool classPool; + boolean wasChanged; + private boolean wasFrozen; + boolean wasPruned; + boolean gcConstPool; // if true, the constant pool entries will be garbage collected. + ClassFile classfile; + byte[] rawClassfile; // backup storage + + private WeakReference memberCache; + private AccessorMaker accessors; + + private FieldInitLink fieldInitializers; + private Hashtable hiddenMethods; // must be synchronous + private int uniqueNumberSeed; + + private boolean doPruning = ClassPool.doPruning; + private int getCount; + private static final int GET_THRESHOLD = 2; // see compress() + + CtClassType(String name, ClassPool cp) { + super(name); + classPool = cp; + wasChanged = wasFrozen = wasPruned = gcConstPool = false; + classfile = null; + rawClassfile = null; + memberCache = null; + accessors = null; + fieldInitializers = null; + hiddenMethods = null; + uniqueNumberSeed = 0; + getCount = 0; + } + + CtClassType(InputStream ins, ClassPool cp) throws IOException { + this((String)null, cp); + classfile = new ClassFile(new DataInputStream(ins)); + qualifiedName = classfile.getName(); + } + + CtClassType(ClassFile cf, ClassPool cp) { + this((String)null, cp); + classfile = cf; + qualifiedName = classfile.getName(); + } + + protected void extendToString(StringBuffer buffer) { + if (wasChanged) + buffer.append("changed "); + + if (wasFrozen) + buffer.append("frozen "); + + if (wasPruned) + buffer.append("pruned "); + + buffer.append(Modifier.toString(getModifiers())); + buffer.append(" class "); + buffer.append(getName()); + + try { + CtClass ext = getSuperclass(); + if (ext != null) { + String name = ext.getName(); + if (!name.equals("java.lang.Object")) + buffer.append(" extends " + ext.getName()); + } + } + catch (NotFoundException e) { + buffer.append(" extends ??"); + } + + try { + CtClass[] intf = getInterfaces(); + if (intf.length > 0) + buffer.append(" implements "); + + for (int i = 0; i < intf.length; ++i) { + buffer.append(intf[i].getName()); + buffer.append(", "); + } + } + catch (NotFoundException e) { + buffer.append(" extends ??"); + } + + CtMember.Cache memCache = getMembers(); + exToString(buffer, " fields=", + memCache.fieldHead(), memCache.lastField()); + exToString(buffer, " constructors=", + memCache.consHead(), memCache.lastCons()); + exToString(buffer, " methods=", + memCache.methodHead(), memCache.lastMethod()); + } + + private void exToString(StringBuffer buffer, String msg, + CtMember head, CtMember tail) { + buffer.append(msg); + while (head != tail) { + head = head.next(); + buffer.append(head); + buffer.append(", "); + } + } + + public AccessorMaker getAccessorMaker() { + if (accessors == null) + accessors = new AccessorMaker(this); + + return accessors; + } + + public ClassFile getClassFile2() { + ClassFile cfile = classfile; + if (cfile != null) + return cfile; + + classPool.compress(); + if (rawClassfile != null) { + try { + classfile = new ClassFile(new DataInputStream( + new ByteArrayInputStream(rawClassfile))); + rawClassfile = null; + getCount = GET_THRESHOLD; + return classfile; + } + catch (IOException e) { + throw new RuntimeException(e.toString(), e); + } + } + + InputStream fin = null; + try { + fin = classPool.openClassfile(getName()); + if (fin == null) + throw new NotFoundException(getName()); + + fin = new BufferedInputStream(fin); + ClassFile cf = new ClassFile(new DataInputStream(fin)); + if (!cf.getName().equals(qualifiedName)) + throw new RuntimeException("cannot find " + qualifiedName + ": " + + cf.getName() + " found in " + + qualifiedName.replace('.', '/') + ".class"); + + classfile = cf; + return cf; + } + catch (NotFoundException e) { + throw new RuntimeException(e.toString(), e); + } + catch (IOException e) { + throw new RuntimeException(e.toString(), e); + } + finally { + if (fin != null) + try { + fin.close(); + } + catch (IOException e) {} + } + } + + /* Inherited from CtClass. Called by get() in ClassPool. + * + * @see javassist.CtClass#incGetCounter() + * @see #toBytecode(DataOutputStream) + */ + final void incGetCounter() { ++getCount; } + + /** + * Invoked from ClassPool#compress(). + * It releases the class files that have not been recently used + * if they are unmodified. + */ + void compress() { + if (getCount < GET_THRESHOLD) + if (!isModified() && ClassPool.releaseUnmodifiedClassFile) + removeClassFile(); + else if (isFrozen() && !wasPruned) + saveClassFile(); + + getCount = 0; + } + + /** + * Converts a ClassFile object into a byte array + * for saving memory space. + */ + private synchronized void saveClassFile() { + /* getMembers() and releaseClassFile() are also synchronized. + */ + if (classfile == null || hasMemberCache() != null) + return; + + ByteArrayOutputStream barray = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(barray); + try { + classfile.write(out); + barray.close(); + rawClassfile = barray.toByteArray(); + classfile = null; + } + catch (IOException e) {} + } + + private synchronized void removeClassFile() { + if (classfile != null && !isModified() && hasMemberCache() == null) + classfile = null; + } + + public ClassPool getClassPool() { return classPool; } + + void setClassPool(ClassPool cp) { classPool = cp; } + + public URL getURL() throws NotFoundException { + URL url = classPool.find(getName()); + if (url == null) + throw new NotFoundException(getName()); + else + return url; + } + + public boolean isModified() { return wasChanged; } + + public boolean isFrozen() { return wasFrozen; } + + public void freeze() { wasFrozen = true; } + + void checkModify() throws RuntimeException { + if (isFrozen()) { + String msg = getName() + " class is frozen"; + if (wasPruned) + msg += " and pruned"; + + throw new RuntimeException(msg); + } + + wasChanged = true; + } + + public void defrost() { + checkPruned("defrost"); + wasFrozen = false; + } + + public boolean subtypeOf(CtClass clazz) throws NotFoundException { + int i; + String cname = clazz.getName(); + if (this == clazz || getName().equals(cname)) + return true; + + ClassFile file = getClassFile2(); + String supername = file.getSuperclass(); + if (supername != null && supername.equals(cname)) + return true; + + String[] ifs = file.getInterfaces(); + int num = ifs.length; + for (i = 0; i < num; ++i) + if (ifs[i].equals(cname)) + return true; + + if (supername != null && classPool.get(supername).subtypeOf(clazz)) + return true; + + for (i = 0; i < num; ++i) + if (classPool.get(ifs[i]).subtypeOf(clazz)) + return true; + + return false; + } + + public void setName(String name) throws RuntimeException { + String oldname = getName(); + if (name.equals(oldname)) + return; + + // check this in advance although classNameChanged() below does. + classPool.checkNotFrozen(name); + ClassFile cf = getClassFile2(); + super.setName(name); + cf.setName(name); + nameReplaced(); + classPool.classNameChanged(oldname, this); + } + + public String getGenericSignature() { + SignatureAttribute sa + = (SignatureAttribute)getClassFile2().getAttribute(SignatureAttribute.tag); + return sa == null ? null : sa.getSignature(); + } + + public void setGenericSignature(String sig) { + ClassFile cf = getClassFile(); + SignatureAttribute sa = new SignatureAttribute(cf.getConstPool(), sig); + cf.addAttribute(sa); + } + + public void replaceClassName(ClassMap classnames) + throws RuntimeException + { + String oldClassName = getName(); + String newClassName + = (String)classnames.get(Descriptor.toJvmName(oldClassName)); + if (newClassName != null) { + newClassName = Descriptor.toJavaName(newClassName); + // check this in advance although classNameChanged() below does. + classPool.checkNotFrozen(newClassName); + } + + super.replaceClassName(classnames); + ClassFile cf = getClassFile2(); + cf.renameClass(classnames); + nameReplaced(); + + if (newClassName != null) { + super.setName(newClassName); + classPool.classNameChanged(oldClassName, this); + } + } + + public void replaceClassName(String oldname, String newname) + throws RuntimeException + { + String thisname = getName(); + if (thisname.equals(oldname)) + setName(newname); + else { + super.replaceClassName(oldname, newname); + getClassFile2().renameClass(oldname, newname); + nameReplaced(); + } + } + + public boolean isInterface() { + return Modifier.isInterface(getModifiers()); + } + + public boolean isAnnotation() { + return Modifier.isAnnotation(getModifiers()); + } + + public boolean isEnum() { + return Modifier.isEnum(getModifiers()); + } + + public int getModifiers() { + ClassFile cf = getClassFile2(); + int acc = cf.getAccessFlags(); + acc = AccessFlag.clear(acc, AccessFlag.SUPER); + int inner = cf.getInnerAccessFlags(); + if (inner != -1 && (inner & AccessFlag.STATIC) != 0) + acc |= AccessFlag.STATIC; + + return AccessFlag.toModifier(acc); + } + + public CtClass[] getNestedClasses() throws NotFoundException { + ClassFile cf = getClassFile2(); + InnerClassesAttribute ica + = (InnerClassesAttribute)cf.getAttribute(InnerClassesAttribute.tag); + if (ica == null) + return new CtClass[0]; + + String thisName = cf.getName() + "$"; + int n = ica.tableLength(); + ArrayList list = new ArrayList(n); + for (int i = 0; i < n; i++) { + String name = ica.innerClass(i); + if (name != null) + if (name.startsWith(thisName)) { + // if it is an immediate nested class + if (name.lastIndexOf('$') < thisName.length()) + list.add(classPool.get(name)); + } + } + + return (CtClass[])list.toArray(new CtClass[list.size()]); + } + + public void setModifiers(int mod) { + ClassFile cf = getClassFile2(); + if (Modifier.isStatic(mod)) { + int flags = cf.getInnerAccessFlags(); + if (flags != -1 && (flags & AccessFlag.STATIC) != 0) + mod = mod & ~Modifier.STATIC; + else + throw new RuntimeException("cannot change " + getName() + " into a static class"); + } + + checkModify(); + cf.setAccessFlags(AccessFlag.of(mod)); + } + + public boolean hasAnnotation(Class clz) { + ClassFile cf = getClassFile2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + cf.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + cf.getAttribute(AnnotationsAttribute.visibleTag); + return hasAnnotationType(clz, getClassPool(), ainfo, ainfo2); + } + + static boolean hasAnnotationType(Class clz, ClassPool cp, + AnnotationsAttribute a1, AnnotationsAttribute a2) + { + Annotation[] anno1, anno2; + + if (a1 == null) + anno1 = null; + else + anno1 = a1.getAnnotations(); + + if (a2 == null) + anno2 = null; + else + anno2 = a2.getAnnotations(); + + String typeName = clz.getName(); + if (anno1 != null) + for (int i = 0; i < anno1.length; i++) + if (anno1[i].getTypeName().equals(typeName)) + return true; + + if (anno2 != null) + for (int i = 0; i < anno2.length; i++) + if (anno2[i].getTypeName().equals(typeName)) + return true; + + return false; + } + + public Object getAnnotation(Class clz) throws ClassNotFoundException { + ClassFile cf = getClassFile2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + cf.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + cf.getAttribute(AnnotationsAttribute.visibleTag); + return getAnnotationType(clz, getClassPool(), ainfo, ainfo2); + } + + static Object getAnnotationType(Class clz, ClassPool cp, + AnnotationsAttribute a1, AnnotationsAttribute a2) + throws ClassNotFoundException + { + Annotation[] anno1, anno2; + + if (a1 == null) + anno1 = null; + else + anno1 = a1.getAnnotations(); + + if (a2 == null) + anno2 = null; + else + anno2 = a2.getAnnotations(); + + String typeName = clz.getName(); + if (anno1 != null) + for (int i = 0; i < anno1.length; i++) + if (anno1[i].getTypeName().equals(typeName)) + return toAnnoType(anno1[i], cp); + + if (anno2 != null) + for (int i = 0; i < anno2.length; i++) + if (anno2[i].getTypeName().equals(typeName)) + return toAnnoType(anno2[i], cp); + + return null; + } + + public Object[] getAnnotations() throws ClassNotFoundException { + return getAnnotations(false); + } + + public Object[] getAvailableAnnotations(){ + try { + return getAnnotations(true); + } + catch (ClassNotFoundException e) { + throw new RuntimeException("Unexpected exception ", e); + } + } + + private Object[] getAnnotations(boolean ignoreNotFound) + throws ClassNotFoundException + { + ClassFile cf = getClassFile2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + cf.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + cf.getAttribute(AnnotationsAttribute.visibleTag); + return toAnnotationType(ignoreNotFound, getClassPool(), ainfo, ainfo2); + } + + static Object[] toAnnotationType(boolean ignoreNotFound, ClassPool cp, + AnnotationsAttribute a1, AnnotationsAttribute a2) + throws ClassNotFoundException + { + Annotation[] anno1, anno2; + int size1, size2; + + if (a1 == null) { + anno1 = null; + size1 = 0; + } + else { + anno1 = a1.getAnnotations(); + size1 = anno1.length; + } + + if (a2 == null) { + anno2 = null; + size2 = 0; + } + else { + anno2 = a2.getAnnotations(); + size2 = anno2.length; + } + + if (!ignoreNotFound){ + Object[] result = new Object[size1 + size2]; + for (int i = 0; i < size1; i++) + result[i] = toAnnoType(anno1[i], cp); + + for (int j = 0; j < size2; j++) + result[j + size1] = toAnnoType(anno2[j], cp); + + return result; + } + else{ + ArrayList annotations = new ArrayList(); + for (int i = 0 ; i < size1 ; i++){ + try{ + annotations.add(toAnnoType(anno1[i], cp)); + } + catch(ClassNotFoundException e){} + } + for (int j = 0; j < size2; j++) { + try{ + annotations.add(toAnnoType(anno2[j], cp)); + } + catch(ClassNotFoundException e){} + } + + return annotations.toArray(); + } + } + + static Object[][] toAnnotationType(boolean ignoreNotFound, ClassPool cp, + ParameterAnnotationsAttribute a1, + ParameterAnnotationsAttribute a2, + MethodInfo minfo) + throws ClassNotFoundException + { + int numParameters = 0; + if (a1 != null) + numParameters = a1.numParameters(); + else if (a2 != null) + numParameters = a2.numParameters(); + else + numParameters = Descriptor.numOfParameters(minfo.getDescriptor()); + + Object[][] result = new Object[numParameters][]; + for (int i = 0; i < numParameters; i++) { + Annotation[] anno1, anno2; + int size1, size2; + + if (a1 == null) { + anno1 = null; + size1 = 0; + } + else { + anno1 = a1.getAnnotations()[i]; + size1 = anno1.length; + } + + if (a2 == null) { + anno2 = null; + size2 = 0; + } + else { + anno2 = a2.getAnnotations()[i]; + size2 = anno2.length; + } + + if (!ignoreNotFound){ + result[i] = new Object[size1 + size2]; + for (int j = 0; j < size1; ++j) + result[i][j] = toAnnoType(anno1[j], cp); + + for (int j = 0; j < size2; ++j) + result[i][j + size1] = toAnnoType(anno2[j], cp); + } + else{ + ArrayList annotations = new ArrayList(); + for (int j = 0 ; j < size1 ; j++){ + try{ + annotations.add(toAnnoType(anno1[j], cp)); + } + catch(ClassNotFoundException e){} + } + for (int j = 0; j < size2; j++){ + try{ + annotations.add(toAnnoType(anno2[j], cp)); + } + catch(ClassNotFoundException e){} + } + + result[i] = annotations.toArray(); + } + } + + return result; + } + + private static Object toAnnoType(Annotation anno, ClassPool cp) + throws ClassNotFoundException + { + try { + ClassLoader cl = cp.getClassLoader(); + return anno.toAnnotationType(cl, cp); + } + catch (ClassNotFoundException e) { + ClassLoader cl2 = cp.getClass().getClassLoader(); + try { + return anno.toAnnotationType(cl2, cp); + } + catch (ClassNotFoundException e2){ + try { + Class clazz = cp.get(anno.getTypeName()).toClass(); + return com.wenshuo.agent.javassist.bytecode.annotation.AnnotationImpl.make( + clazz.getClassLoader(), + clazz, cp, anno); + } + catch (Throwable e3) { + throw new ClassNotFoundException(anno.getTypeName()); + } + } + } + } + + public boolean subclassOf(CtClass superclass) { + if (superclass == null) + return false; + + String superName = superclass.getName(); + CtClass curr = this; + try { + while (curr != null) { + if (curr.getName().equals(superName)) + return true; + + curr = curr.getSuperclass(); + } + } + catch (Exception ignored) {} + return false; + } + + public CtClass getSuperclass() throws NotFoundException { + String supername = getClassFile2().getSuperclass(); + if (supername == null) + return null; + else + return classPool.get(supername); + } + + public void setSuperclass(CtClass clazz) throws CannotCompileException { + checkModify(); + if (isInterface()) + addInterface(clazz); + else + getClassFile2().setSuperclass(clazz.getName()); + } + + public CtClass[] getInterfaces() throws NotFoundException { + String[] ifs = getClassFile2().getInterfaces(); + int num = ifs.length; + CtClass[] ifc = new CtClass[num]; + for (int i = 0; i < num; ++i) + ifc[i] = classPool.get(ifs[i]); + + return ifc; + } + + public void setInterfaces(CtClass[] list) { + checkModify(); + String[] ifs; + if (list == null) + ifs = new String[0]; + else { + int num = list.length; + ifs = new String[num]; + for (int i = 0; i < num; ++i) + ifs[i] = list[i].getName(); + } + + getClassFile2().setInterfaces(ifs); + } + + public void addInterface(CtClass anInterface) { + checkModify(); + if (anInterface != null) + getClassFile2().addInterface(anInterface.getName()); + } + + public CtClass getDeclaringClass() throws NotFoundException { + ClassFile cf = getClassFile2(); + InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( + InnerClassesAttribute.tag); + if (ica == null) + return null; + + String name = getName(); + int n = ica.tableLength(); + for (int i = 0; i < n; ++i) + if (name.equals(ica.innerClass(i))) { + String outName = ica.outerClass(i); + if (outName != null) + return classPool.get(outName); + else { + // maybe anonymous or local class. + EnclosingMethodAttribute ema + = (EnclosingMethodAttribute)cf.getAttribute( + EnclosingMethodAttribute.tag); + if (ema != null) + return classPool.get(ema.className()); + } + } + + return null; + } + + public CtBehavior getEnclosingBehavior() throws NotFoundException { + ClassFile cf = getClassFile2(); + EnclosingMethodAttribute ema + = (EnclosingMethodAttribute)cf.getAttribute( + EnclosingMethodAttribute.tag); + if (ema == null) + return null; + else { + CtClass enc = classPool.get(ema.className()); + String name = ema.methodName(); + if (MethodInfo.nameInit.equals(name)) + return enc.getConstructor(ema.methodDescriptor()); + else if(MethodInfo.nameClinit.equals(name)) + return enc.getClassInitializer(); + else + return enc.getMethod(name, ema.methodDescriptor()); + } + } + + public CtClass makeNestedClass(String name, boolean isStatic) { + if (!isStatic) + throw new RuntimeException( + "sorry, only nested static class is supported"); + + checkModify(); + CtClass c = classPool.makeNestedClass(getName() + "$" + name); + ClassFile cf = getClassFile2(); + ClassFile cf2 = c.getClassFile2(); + InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( + InnerClassesAttribute.tag); + if (ica == null) { + ica = new InnerClassesAttribute(cf.getConstPool()); + cf.addAttribute(ica); + } + + ica.append(c.getName(), this.getName(), name, + (cf2.getAccessFlags() & ~AccessFlag.SUPER) | AccessFlag.STATIC); + cf2.addAttribute(ica.copy(cf2.getConstPool(), null)); + return c; + } + + /* flush cached names. + */ + private void nameReplaced() { + CtMember.Cache cache = hasMemberCache(); + if (cache != null) { + CtMember mth = cache.methodHead(); + CtMember tail = cache.lastMethod(); + while (mth != tail) { + mth = mth.next(); + mth.nameReplaced(); + } + } + } + + /** + * Returns null if members are not cached. + */ + protected CtMember.Cache hasMemberCache() { + if (memberCache != null) + return (CtMember.Cache)memberCache.get(); + else + return null; + } + + protected synchronized CtMember.Cache getMembers() { + CtMember.Cache cache = null; + if (memberCache == null + || (cache = (CtMember.Cache)memberCache.get()) == null) { + cache = new CtMember.Cache(this); + makeFieldCache(cache); + makeBehaviorCache(cache); + memberCache = new WeakReference(cache); + } + + return cache; + } + + private void makeFieldCache(CtMember.Cache cache) { + List list = getClassFile2().getFields(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + CtField newField = new CtField(finfo, this); + cache.addField(newField); + } + } + + private void makeBehaviorCache(CtMember.Cache cache) { + List list = getClassFile2().getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + if (minfo.isMethod()) { + CtMethod newMethod = new CtMethod(minfo, this); + cache.addMethod(newMethod); + } + else { + CtConstructor newCons = new CtConstructor(minfo, this); + cache.addConstructor(newCons); + } + } + } + + public CtField[] getFields() { + ArrayList alist = new ArrayList(); + getFields(alist, this); + return (CtField[])alist.toArray(new CtField[alist.size()]); + } + + private static void getFields(ArrayList alist, CtClass cc) { + int i, num; + if (cc == null) + return; + + try { + getFields(alist, cc.getSuperclass()); + } + catch (NotFoundException e) {} + + try { + CtClass[] ifs = cc.getInterfaces(); + num = ifs.length; + for (i = 0; i < num; ++i) + getFields(alist, ifs[i]); + } + catch (NotFoundException e) {} + + CtMember.Cache memCache = ((CtClassType)cc).getMembers(); + CtMember field = memCache.fieldHead(); + CtMember tail = memCache.lastField(); + while (field != tail) { + field = field.next(); + if (!Modifier.isPrivate(field.getModifiers())) + alist.add(field); + } + } + + public CtField getField(String name, String desc) throws NotFoundException { + CtField f = getField2(name, desc); + return checkGetField(f, name, desc); + } + + private CtField checkGetField(CtField f, String name, String desc) + throws NotFoundException + { + if (f == null) { + String msg = "field: " + name; + if (desc != null) + msg += " type " + desc; + + throw new NotFoundException(msg + " in " + getName()); + } + else + return f; + } + + CtField getField2(String name, String desc) { + CtField df = getDeclaredField2(name, desc); + if (df != null) + return df; + + try { + CtClass[] ifs = getInterfaces(); + int num = ifs.length; + for (int i = 0; i < num; ++i) { + CtField f = ifs[i].getField2(name, desc); + if (f != null) + return f; + } + + CtClass s = getSuperclass(); + if (s != null) + return s.getField2(name, desc); + } + catch (NotFoundException e) {} + return null; + } + + public CtField[] getDeclaredFields() { + CtMember.Cache memCache = getMembers(); + CtMember field = memCache.fieldHead(); + CtMember tail = memCache.lastField(); + int num = CtMember.Cache.count(field, tail); + CtField[] cfs = new CtField[num]; + int i = 0; + while (field != tail) { + field = field.next(); + cfs[i++] = (CtField)field; + } + + return cfs; + } + + public CtField getDeclaredField(String name) throws NotFoundException { + return getDeclaredField(name, null); + } + + public CtField getDeclaredField(String name, String desc) throws NotFoundException { + CtField f = getDeclaredField2(name, desc); + return checkGetField(f, name, desc); + } + + private CtField getDeclaredField2(String name, String desc) { + CtMember.Cache memCache = getMembers(); + CtMember field = memCache.fieldHead(); + CtMember tail = memCache.lastField(); + while (field != tail) { + field = field.next(); + if (field.getName().equals(name) + && (desc == null || desc.equals(field.getSignature()))) + return (CtField)field; + } + + return null; + } + + public CtBehavior[] getDeclaredBehaviors() { + CtMember.Cache memCache = getMembers(); + CtMember cons = memCache.consHead(); + CtMember consTail = memCache.lastCons(); + int cnum = CtMember.Cache.count(cons, consTail); + CtMember mth = memCache.methodHead(); + CtMember mthTail = memCache.lastMethod(); + int mnum = CtMember.Cache.count(mth, mthTail); + + CtBehavior[] cb = new CtBehavior[cnum + mnum]; + int i = 0; + while (cons != consTail) { + cons = cons.next(); + cb[i++] = (CtBehavior)cons; + } + + while (mth != mthTail) { + mth = mth.next(); + cb[i++] = (CtBehavior)mth; + } + + return cb; + } + + public CtConstructor[] getConstructors() { + CtMember.Cache memCache = getMembers(); + CtMember cons = memCache.consHead(); + CtMember consTail = memCache.lastCons(); + + int n = 0; + CtMember mem = cons; + while (mem != consTail) { + mem = mem.next(); + if (isPubCons((CtConstructor)mem)) + n++; + } + + CtConstructor[] result = new CtConstructor[n]; + int i = 0; + mem = cons; + while (mem != consTail) { + mem = mem.next(); + CtConstructor cc = (CtConstructor)mem; + if (isPubCons(cc)) + result[i++] = cc; + } + + return result; + } + + private static boolean isPubCons(CtConstructor cons) { + return !Modifier.isPrivate(cons.getModifiers()) + && cons.isConstructor(); + } + + public CtConstructor getConstructor(String desc) + throws NotFoundException + { + CtMember.Cache memCache = getMembers(); + CtMember cons = memCache.consHead(); + CtMember consTail = memCache.lastCons(); + + while (cons != consTail) { + cons = cons.next(); + CtConstructor cc = (CtConstructor)cons; + if (cc.getMethodInfo2().getDescriptor().equals(desc) + && cc.isConstructor()) + return cc; + } + + return super.getConstructor(desc); + } + + public CtConstructor[] getDeclaredConstructors() { + CtMember.Cache memCache = getMembers(); + CtMember cons = memCache.consHead(); + CtMember consTail = memCache.lastCons(); + + int n = 0; + CtMember mem = cons; + while (mem != consTail) { + mem = mem.next(); + CtConstructor cc = (CtConstructor)mem; + if (cc.isConstructor()) + n++; + } + + CtConstructor[] result = new CtConstructor[n]; + int i = 0; + mem = cons; + while (mem != consTail) { + mem = mem.next(); + CtConstructor cc = (CtConstructor)mem; + if (cc.isConstructor()) + result[i++] = cc; + } + + return result; + } + + public CtConstructor getClassInitializer() { + CtMember.Cache memCache = getMembers(); + CtMember cons = memCache.consHead(); + CtMember consTail = memCache.lastCons(); + + while (cons != consTail) { + cons = cons.next(); + CtConstructor cc = (CtConstructor)cons; + if (cc.isClassInitializer()) + return cc; + } + + return null; + } + + public CtMethod[] getMethods() { + HashMap h = new HashMap(); + getMethods0(h, this); + return (CtMethod[])h.values().toArray(new CtMethod[h.size()]); + } + + private static void getMethods0(HashMap h, CtClass cc) { + try { + CtClass[] ifs = cc.getInterfaces(); + int size = ifs.length; + for (int i = 0; i < size; ++i) + getMethods0(h, ifs[i]); + } + catch (NotFoundException e) {} + + try { + CtClass s = cc.getSuperclass(); + if (s != null) + getMethods0(h, s); + } + catch (NotFoundException e) {} + + if (cc instanceof CtClassType) { + CtMember.Cache memCache = ((CtClassType)cc).getMembers(); + CtMember mth = memCache.methodHead(); + CtMember mthTail = memCache.lastMethod(); + + while (mth != mthTail) { + mth = mth.next(); + if (!Modifier.isPrivate(mth.getModifiers())) + h.put(((CtMethod)mth).getStringRep(), mth); + } + } + } + + public CtMethod getMethod(String name, String desc) + throws NotFoundException + { + CtMethod m = getMethod0(this, name, desc); + if (m != null) + return m; + else + throw new NotFoundException(name + "(..) is not found in " + + getName()); + } + + private static CtMethod getMethod0(CtClass cc, + String name, String desc) { + if (cc instanceof CtClassType) { + CtMember.Cache memCache = ((CtClassType)cc).getMembers(); + CtMember mth = memCache.methodHead(); + CtMember mthTail = memCache.lastMethod(); + + while (mth != mthTail) { + mth = mth.next(); + if (mth.getName().equals(name) + && ((CtMethod)mth).getMethodInfo2().getDescriptor().equals(desc)) + return (CtMethod)mth; + } + } + + try { + CtClass s = cc.getSuperclass(); + if (s != null) { + CtMethod m = getMethod0(s, name, desc); + if (m != null) + return m; + } + } + catch (NotFoundException e) {} + + try { + CtClass[] ifs = cc.getInterfaces(); + int size = ifs.length; + for (int i = 0; i < size; ++i) { + CtMethod m = getMethod0(ifs[i], name, desc); + if (m != null) + return m; + } + } + catch (NotFoundException e) {} + return null; + } + + public CtMethod[] getDeclaredMethods() { + CtMember.Cache memCache = getMembers(); + CtMember mth = memCache.methodHead(); + CtMember mthTail = memCache.lastMethod(); + int num = CtMember.Cache.count(mth, mthTail); + CtMethod[] cms = new CtMethod[num]; + int i = 0; + while (mth != mthTail) { + mth = mth.next(); + cms[i++] = (CtMethod)mth; + } + + return cms; + } + + public CtMethod[] getDeclaredMethods(String name) throws NotFoundException { + CtMember.Cache memCache = getMembers(); + CtMember mth = memCache.methodHead(); + CtMember mthTail = memCache.lastMethod(); + ArrayList methods = new ArrayList(); + while (mth != mthTail) { + mth = mth.next(); + if (mth.getName().equals(name)) + methods.add((CtMethod)mth); + } + + return methods.toArray(new CtMethod[methods.size()]); + } + + public CtMethod getDeclaredMethod(String name) throws NotFoundException { + CtMember.Cache memCache = getMembers(); + CtMember mth = memCache.methodHead(); + CtMember mthTail = memCache.lastMethod(); + while (mth != mthTail) { + mth = mth.next(); + if (mth.getName().equals(name)) + return (CtMethod)mth; + } + + throw new NotFoundException(name + "(..) is not found in " + + getName()); + } + + public CtMethod getDeclaredMethod(String name, CtClass[] params) + throws NotFoundException + { + String desc = Descriptor.ofParameters(params); + CtMember.Cache memCache = getMembers(); + CtMember mth = memCache.methodHead(); + CtMember mthTail = memCache.lastMethod(); + + while (mth != mthTail) { + mth = mth.next(); + if (mth.getName().equals(name) + && ((CtMethod)mth).getMethodInfo2().getDescriptor().startsWith(desc)) + return (CtMethod)mth; + } + + throw new NotFoundException(name + "(..) is not found in " + + getName()); + } + + public void addField(CtField f, String init) + throws CannotCompileException + { + addField(f, CtField.Initializer.byExpr(init)); + } + + public void addField(CtField f, CtField.Initializer init) + throws CannotCompileException + { + checkModify(); + if (f.getDeclaringClass() != this) + throw new CannotCompileException("cannot add"); + + if (init == null) + init = f.getInit(); + + if (init != null) { + init.check(f.getSignature()); + int mod = f.getModifiers(); + if (Modifier.isStatic(mod) && Modifier.isFinal(mod)) + try { + ConstPool cp = getClassFile2().getConstPool(); + int index = init.getConstantValue(cp, f.getType()); + if (index != 0) { + f.getFieldInfo2().addAttribute(new ConstantAttribute(cp, index)); + init = null; + } + } + catch (NotFoundException e) {} + } + + getMembers().addField(f); + getClassFile2().addField(f.getFieldInfo2()); + + if (init != null) { + FieldInitLink fil = new FieldInitLink(f, init); + FieldInitLink link = fieldInitializers; + if (link == null) + fieldInitializers = fil; + else { + while (link.next != null) + link = link.next; + + link.next = fil; + } + } + } + + public void removeField(CtField f) throws NotFoundException { + checkModify(); + FieldInfo fi = f.getFieldInfo2(); + ClassFile cf = getClassFile2(); + if (cf.getFields().remove(fi)) { + getMembers().remove(f); + gcConstPool = true; + } + else + throw new NotFoundException(f.toString()); + } + + public CtConstructor makeClassInitializer() + throws CannotCompileException + { + CtConstructor clinit = getClassInitializer(); + if (clinit != null) + return clinit; + + checkModify(); + ClassFile cf = getClassFile2(); + Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); + modifyClassConstructor(cf, code, 0, 0); + return getClassInitializer(); + } + + public void addConstructor(CtConstructor c) + throws CannotCompileException + { + checkModify(); + if (c.getDeclaringClass() != this) + throw new CannotCompileException("cannot add"); + + getMembers().addConstructor(c); + getClassFile2().addMethod(c.getMethodInfo2()); + } + + public void removeConstructor(CtConstructor m) throws NotFoundException { + checkModify(); + MethodInfo mi = m.getMethodInfo2(); + ClassFile cf = getClassFile2(); + if (cf.getMethods().remove(mi)) { + getMembers().remove(m); + gcConstPool = true; + } + else + throw new NotFoundException(m.toString()); + } + + public void addMethod(CtMethod m) throws CannotCompileException { + checkModify(); + if (m.getDeclaringClass() != this) + throw new CannotCompileException("bad declaring class"); + + int mod = m.getModifiers(); + if ((getModifiers() & Modifier.INTERFACE) != 0) { + m.setModifiers(mod | Modifier.PUBLIC); + if ((mod & Modifier.ABSTRACT) == 0) + throw new CannotCompileException( + "an interface method must be abstract: " + m.toString()); + } + + getMembers().addMethod(m); + getClassFile2().addMethod(m.getMethodInfo2()); + if ((mod & Modifier.ABSTRACT) != 0) + setModifiers(getModifiers() | Modifier.ABSTRACT); + } + + public void removeMethod(CtMethod m) throws NotFoundException { + checkModify(); + MethodInfo mi = m.getMethodInfo2(); + ClassFile cf = getClassFile2(); + if (cf.getMethods().remove(mi)) { + getMembers().remove(m); + gcConstPool = true; + } + else + throw new NotFoundException(m.toString()); + } + + public byte[] getAttribute(String name) { + AttributeInfo ai = getClassFile2().getAttribute(name); + if (ai == null) + return null; + else + return ai.get(); + } + + public void setAttribute(String name, byte[] data) { + checkModify(); + ClassFile cf = getClassFile2(); + cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data)); + } + + public void instrument(CodeConverter converter) + throws CannotCompileException + { + checkModify(); + ClassFile cf = getClassFile2(); + ConstPool cp = cf.getConstPool(); + List list = cf.getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + converter.doit(this, minfo, cp); + } + } + + public void instrument(ExprEditor editor) + throws CannotCompileException + { + checkModify(); + ClassFile cf = getClassFile2(); + List list = cf.getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + editor.doit(this, minfo); + } + } + + /** + * @see javassist.CtClass#prune() + * @see javassist.CtClass#stopPruning(boolean) + */ + public void prune() { + if (wasPruned) + return; + + wasPruned = wasFrozen = true; + getClassFile2().prune(); + } + + public void rebuildClassFile() { gcConstPool = true; } + + public void toBytecode(DataOutputStream out) + throws CannotCompileException, IOException + { + try { + if (isModified()) { + checkPruned("toBytecode"); + ClassFile cf = getClassFile2(); + if (gcConstPool) { + cf.compact(); + gcConstPool = false; + } + + modifyClassConstructor(cf); + modifyConstructors(cf); + if (debugDump != null) + dumpClassFile(cf); + + cf.write(out); + out.flush(); + fieldInitializers = null; + if (doPruning) { + // to save memory + cf.prune(); + wasPruned = true; + } + } + else { + classPool.writeClassfile(getName(), out); + // to save memory + // classfile = null; + } + + getCount = 0; + wasFrozen = true; + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (IOException e) { + throw new CannotCompileException(e); + } + } + + private void dumpClassFile(ClassFile cf) throws IOException { + DataOutputStream dump = makeFileOutput(debugDump); + try { + cf.write(dump); + } + finally { + dump.close(); + } + } + + /* See also checkModified() + */ + private void checkPruned(String method) { + if (wasPruned) + throw new RuntimeException(method + "(): " + getName() + + " was pruned."); + } + + public boolean stopPruning(boolean stop) { + boolean prev = !doPruning; + doPruning = !stop; + return prev; + } + + private void modifyClassConstructor(ClassFile cf) + throws CannotCompileException, NotFoundException + { + if (fieldInitializers == null) + return; + + Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); + Javac jv = new Javac(code, this); + int stacksize = 0; + boolean doInit = false; + for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) { + CtField f = fi.field; + if (Modifier.isStatic(f.getModifiers())) { + doInit = true; + int s = fi.init.compileIfStatic(f.getType(), f.getName(), + code, jv); + if (stacksize < s) + stacksize = s; + } + } + + if (doInit) // need an initializer for static fileds. + modifyClassConstructor(cf, code, stacksize, 0); + } + + private void modifyClassConstructor(ClassFile cf, Bytecode code, + int stacksize, int localsize) + throws CannotCompileException + { + MethodInfo m = cf.getStaticInitializer(); + if (m == null) { + code.add(Bytecode.RETURN); + code.setMaxStack(stacksize); + code.setMaxLocals(localsize); + m = new MethodInfo(cf.getConstPool(), "", "()V"); + m.setAccessFlags(AccessFlag.STATIC); + m.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(m); + CtMember.Cache cache = hasMemberCache(); + if (cache != null) + cache.addConstructor(new CtConstructor(m, this)); + } + else { + CodeAttribute codeAttr = m.getCodeAttribute(); + if (codeAttr == null) + throw new CannotCompileException("empty "); + + try { + CodeIterator it = codeAttr.iterator(); + int pos = it.insertEx(code.get()); + it.insert(code.getExceptionTable(), pos); + int maxstack = codeAttr.getMaxStack(); + if (maxstack < stacksize) + codeAttr.setMaxStack(stacksize); + + int maxlocals = codeAttr.getMaxLocals(); + if (maxlocals < localsize) + codeAttr.setMaxLocals(localsize); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + try { + m.rebuildStackMapIf6(classPool, cf); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + private void modifyConstructors(ClassFile cf) + throws CannotCompileException, NotFoundException + { + if (fieldInitializers == null) + return; + + ConstPool cp = cf.getConstPool(); + List list = cf.getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + if (minfo.isConstructor()) { + CodeAttribute codeAttr = minfo.getCodeAttribute(); + if (codeAttr != null) + try { + Bytecode init = new Bytecode(cp, 0, + codeAttr.getMaxLocals()); + CtClass[] params + = Descriptor.getParameterTypes( + minfo.getDescriptor(), + classPool); + int stacksize = makeFieldInitializer(init, params); + insertAuxInitializer(codeAttr, init, stacksize); + minfo.rebuildStackMapIf6(classPool, cf); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + } + } + + private static void insertAuxInitializer(CodeAttribute codeAttr, + Bytecode initializer, + int stacksize) + throws BadBytecode + { + CodeIterator it = codeAttr.iterator(); + int index = it.skipSuperConstructor(); + if (index < 0) { + index = it.skipThisConstructor(); + if (index >= 0) + return; // this() is called. + + // Neither this() or super() is called. + } + + int pos = it.insertEx(initializer.get()); + it.insert(initializer.getExceptionTable(), pos); + int maxstack = codeAttr.getMaxStack(); + if (maxstack < stacksize) + codeAttr.setMaxStack(stacksize); + } + + private int makeFieldInitializer(Bytecode code, CtClass[] parameters) + throws CannotCompileException, NotFoundException + { + int stacksize = 0; + Javac jv = new Javac(code, this); + try { + jv.recordParams(parameters, false); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + + for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) { + CtField f = fi.field; + if (!Modifier.isStatic(f.getModifiers())) { + int s = fi.init.compile(f.getType(), f.getName(), code, + parameters, jv); + if (stacksize < s) + stacksize = s; + } + } + + return stacksize; + } + + // Methods used by CtNewWrappedMethod + + Hashtable getHiddenMethods() { + if (hiddenMethods == null) + hiddenMethods = new Hashtable(); + + return hiddenMethods; + } + + int getUniqueNumber() { return uniqueNumberSeed++; } + + public String makeUniqueName(String prefix) { + HashMap table = new HashMap(); + makeMemberList(table); + Set keys = table.keySet(); + String[] methods = new String[keys.size()]; + keys.toArray(methods); + + if (notFindInArray(prefix, methods)) + return prefix; + + int i = 100; + String name; + do { + if (i > 999) + throw new RuntimeException("too many unique name"); + + name = prefix + i++; + } while (!notFindInArray(name, methods)); + return name; + } + + private static boolean notFindInArray(String prefix, String[] values) { + int len = values.length; + for (int i = 0; i < len; i++) + if (values[i].startsWith(prefix)) + return false; + + return true; + } + + private void makeMemberList(HashMap table) { + int mod = getModifiers(); + if (Modifier.isAbstract(mod) || Modifier.isInterface(mod)) + try { + CtClass[] ifs = getInterfaces(); + int size = ifs.length; + for (int i = 0; i < size; i++) { + CtClass ic =ifs[i]; + if (ic != null && ic instanceof CtClassType) + ((CtClassType)ic).makeMemberList(table); + } + } + catch (NotFoundException e) {} + + try { + CtClass s = getSuperclass(); + if (s != null && s instanceof CtClassType) + ((CtClassType)s).makeMemberList(table); + } + catch (NotFoundException e) {} + + List list = getClassFile2().getMethods(); + int n = list.size(); + for (int i = 0; i < n; i++) { + MethodInfo minfo = (MethodInfo)list.get(i); + table.put(minfo.getName(), this); + } + + list = getClassFile2().getFields(); + n = list.size(); + for (int i = 0; i < n; i++) { + FieldInfo finfo = (FieldInfo)list.get(i); + table.put(finfo.getName(), this); + } + } +} + +class FieldInitLink { + FieldInitLink next; + CtField field; + CtField.Initializer init; + + FieldInitLink(CtField f, CtField.Initializer i) { + next = null; + field = f; + init = i; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtConstructor.java b/src/main/java/com/wenshuo/agent/javassist/CtConstructor.java new file mode 100644 index 0000000..58dd659 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtConstructor.java @@ -0,0 +1,403 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.Javac; +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * An instance of CtConstructor represents a constructor. + * It may represent a static constructor + * (class initializer). To distinguish a constructor and a class + * initializer, call isClassInitializer(). + * + *

See the super class CtBehavior as well since + * a number of useful methods are in CtBehavior. + * + * @see CtClass#getDeclaredConstructors() + * @see CtClass#getClassInitializer() + * @see CtNewConstructor + */ +public final class CtConstructor extends CtBehavior { + protected CtConstructor(MethodInfo minfo, CtClass declaring) { + super(declaring, minfo); + } + + /** + * Creates a constructor with no constructor body. + * The created constructor + * must be added to a class with CtClass.addConstructor(). + * + *

The created constructor does not include a constructor body, + * which must be specified with setBody(). + * + * @param declaring the class to which the created method is added. + * @param parameters a list of the parameter types + * + * @see CtClass#addConstructor(CtConstructor) + * @see CtConstructor#setBody(String) + * @see CtConstructor#setBody(CtConstructor,ClassMap) + */ + public CtConstructor(CtClass[] parameters, CtClass declaring) { + this((MethodInfo)null, declaring); + ConstPool cp = declaring.getClassFile2().getConstPool(); + String desc = Descriptor.ofConstructor(parameters); + methodInfo = new MethodInfo(cp, "", desc); + setModifiers(Modifier.PUBLIC); + } + + /** + * Creates a copy of a CtConstructor object. + * The created constructor must be + * added to a class with CtClass.addConstructor(). + * + *

All occurrences of class names in the created constructor + * are replaced with names specified by + * map if map is not null. + * + *

By default, all the occurrences of the names of the class + * declaring src and the superclass are replaced + * with the name of the class and the superclass that + * the created constructor is added to. + * This is done whichever map is null or not. + * To prevent this replacement, call ClassMap.fix() + * or put() to explicitly specify replacement. + * + *

Note: if the .class notation (for example, + * String.class) is included in an expression, the + * Javac compiler may produce a helper method. + * Since this constructor never + * copies this helper method, the programmers have the responsiblity of + * copying it. Otherwise, use Class.forName() in the + * expression. + * + * @param src the source method. + * @param declaring the class to which the created method is added. + * @param map the hashtable associating original class names + * with substituted names. + * It can be null. + * + * @see CtClass#addConstructor(CtConstructor) + * @see ClassMap#fix(String) + */ + public CtConstructor(CtConstructor src, CtClass declaring, ClassMap map) + throws CannotCompileException + { + this((MethodInfo)null, declaring); + copy(src, true, map); + } + + /** + * Returns true if this object represents a constructor. + */ + public boolean isConstructor() { + return methodInfo.isConstructor(); + } + + /** + * Returns true if this object represents a static initializer. + */ + public boolean isClassInitializer() { + return methodInfo.isStaticInitializer(); + } + + /** + * Returns the constructor name followed by parameter types + * such as javassist.CtConstructor(CtClass[],CtClass). + * + * @since 3.5 + */ + public String getLongName() { + return getDeclaringClass().getName() + + (isConstructor() ? Descriptor.toString(getSignature()) + : ("." + MethodInfo.nameClinit + "()")); + } + + /** + * Obtains the name of this constructor. + * It is the same as the simple name of the class declaring this + * constructor. If this object represents a class initializer, + * then this method returns "<clinit>". + */ + public String getName() { + if (methodInfo.isStaticInitializer()) + return MethodInfo.nameClinit; + else + return declaringClass.getSimpleName(); + } + + /** + * Returns true if the constructor (or static initializer) + * is the default one. This method returns true if the constructor + * takes some arguments but it does not perform anything except + * calling super() (the no-argument constructor of + * the super class). + */ + public boolean isEmpty() { + CodeAttribute ca = getMethodInfo2().getCodeAttribute(); + if (ca == null) + return false; // native or abstract?? + // they are not allowed, though. + + ConstPool cp = ca.getConstPool(); + CodeIterator it = ca.iterator(); + try { + int pos, desc; + int op0 = it.byteAt(it.next()); + return op0 == Opcode.RETURN // empty static initializer + || (op0 == Opcode.ALOAD_0 + && it.byteAt(pos = it.next()) == Opcode.INVOKESPECIAL + && (desc = cp.isConstructor(getSuperclassName(), + it.u16bitAt(pos + 1))) != 0 + && "()V".equals(cp.getUtf8Info(desc)) + && it.byteAt(it.next()) == Opcode.RETURN + && !it.hasNext()); + } + catch (BadBytecode e) {} + return false; + } + + private String getSuperclassName() { + ClassFile cf = declaringClass.getClassFile2(); + return cf.getSuperclass(); + } + + /** + * Returns true if this constructor calls a constructor + * of the super class. This method returns false if it + * calls another constructor of this class by this(). + */ + public boolean callsSuper() throws CannotCompileException { + CodeAttribute codeAttr = methodInfo.getCodeAttribute(); + if (codeAttr != null) { + CodeIterator it = codeAttr.iterator(); + try { + int index = it.skipSuperConstructor(); + return index >= 0; + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + return false; + } + + /** + * Sets a constructor body. + * + * @param src the source code representing the constructor body. + * It must be a single statement or block. + * If it is null, the substituted + * constructor body does nothing except calling + * super(). + */ + public void setBody(String src) throws CannotCompileException { + if (src == null) + if (isClassInitializer()) + src = ";"; + else + src = "super();"; + + super.setBody(src); + } + + /** + * Copies a constructor body from another constructor. + * + *

All occurrences of the class names in the copied body + * are replaced with the names specified by + * map if map is not null. + * + * @param src the method that the body is copied from. + * @param map the hashtable associating original class names + * with substituted names. + * It can be null. + */ + public void setBody(CtConstructor src, ClassMap map) + throws CannotCompileException + { + setBody0(src.declaringClass, src.methodInfo, + declaringClass, methodInfo, map); + } + + /** + * Inserts bytecode just after another constructor in the super class + * or this class is called. + * It does not work if this object represents a class initializer. + * + * @param src the source code representing the inserted bytecode. + * It must be a single statement or block. + */ + public void insertBeforeBody(String src) throws CannotCompileException { + CtClass cc = declaringClass; + cc.checkModify(); + if (isClassInitializer()) + throw new CannotCompileException("class initializer"); + + CodeAttribute ca = methodInfo.getCodeAttribute(); + CodeIterator iterator = ca.iterator(); + Bytecode b = new Bytecode(methodInfo.getConstPool(), + ca.getMaxStack(), ca.getMaxLocals()); + b.setStackDepth(ca.getMaxStack()); + Javac jv = new Javac(b, cc); + try { + jv.recordParams(getParameterTypes(), false); + jv.compileStmnt(src); + ca.setMaxStack(b.getMaxStack()); + ca.setMaxLocals(b.getMaxLocals()); + iterator.skipConstructor(); + int pos = iterator.insertEx(b.get()); + iterator.insert(b.getExceptionTable(), pos); + methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + /* This method is called by addCatch() in CtBehavior. + * super() and this() must not be in a try statement. + */ + int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException { + CodeIterator ci = ca.iterator(); + try { + ci.skipConstructor(); + return ci.next(); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + /** + * Makes a copy of this constructor and converts it into a method. + * The signature of the mehtod is the same as the that of this constructor. + * The return type is void. The resulting method must be + * appended to the class specified by declaring. + * If this constructor is a static initializer, the resulting method takes + * no parameter. + * + *

An occurrence of another constructor call this() + * or a super constructor call super() is + * eliminated from the resulting method. + * + *

The immediate super class of the class declaring this constructor + * must be also a super class of the class declaring the resulting method. + * If the constructor accesses a field, the class declaring the resulting method + * must also declare a field with the same name and type. + * + * @param name the name of the resulting method. + * @param declaring the class declaring the resulting method. + */ + public CtMethod toMethod(String name, CtClass declaring) + throws CannotCompileException + { + return toMethod(name, declaring, null); + } + + /** + * Makes a copy of this constructor and converts it into a method. + * The signature of the method is the same as the that of this constructor. + * The return type is void. The resulting method must be + * appended to the class specified by declaring. + * If this constructor is a static initializer, the resulting method takes + * no parameter. + * + *

An occurrence of another constructor call this() + * or a super constructor call super() is + * eliminated from the resulting method. + * + *

The immediate super class of the class declaring this constructor + * must be also a super class of the class declaring the resulting method + * (this is obviously true if the second parameter declaring is + * the same as the class declaring this constructor). + * If the constructor accesses a field, the class declaring the resulting method + * must also declare a field with the same name and type. + * + * @param name the name of the resulting method. + * @param declaring the class declaring the resulting method. + * It is normally the same as the class declaring this + * constructor. + * @param map the hash table associating original class names + * with substituted names. The original class names will be + * replaced while making a copy. + * map can be null. + */ + public CtMethod toMethod(String name, CtClass declaring, ClassMap map) + throws CannotCompileException + { + CtMethod method = new CtMethod(null, declaring); + method.copy(this, false, map); + if (isConstructor()) { + MethodInfo minfo = method.getMethodInfo2(); + CodeAttribute ca = minfo.getCodeAttribute(); + if (ca != null) { + removeConsCall(ca); + try { + methodInfo.rebuildStackMapIf6(declaring.getClassPool(), + declaring.getClassFile2()); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + } + + method.setName(name); + return method; + } + + private static void removeConsCall(CodeAttribute ca) + throws CannotCompileException + { + CodeIterator iterator = ca.iterator(); + try { + int pos = iterator.skipConstructor(); + if (pos >= 0) { + int mref = iterator.u16bitAt(pos + 1); + String desc = ca.getConstPool().getMethodrefType(mref); + int num = Descriptor.numOfParameters(desc) + 1; + if (num > 3) + pos = iterator.insertGapAt(pos, num - 3, false).position; + + iterator.writeByte(Opcode.POP, pos++); // this + iterator.writeByte(Opcode.NOP, pos); + iterator.writeByte(Opcode.NOP, pos + 1); + Descriptor.Iterator it = new Descriptor.Iterator(desc); + while (true) { + it.next(); + if (it.isParameter()) + iterator.writeByte(it.is2byte() ? Opcode.POP2 : Opcode.POP, + pos++); + else + break; + } + } + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtField.java b/src/main/java/com/wenshuo/agent/javassist/CtField.java new file mode 100644 index 0000000..7489d83 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtField.java @@ -0,0 +1,1406 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.Javac; +import com.wenshuo.agent.javassist.compiler.SymbolTable; +import com.wenshuo.agent.javassist.compiler.CompileError; +import com.wenshuo.agent.javassist.compiler.ast.ASTree; +import com.wenshuo.agent.javassist.compiler.ast.IntConst; +import com.wenshuo.agent.javassist.compiler.ast.DoubleConst; +import com.wenshuo.agent.javassist.compiler.ast.StringL; + +/** + * An instance of CtField represents a field. + * + * @see CtClass#getDeclaredFields() + */ +public class CtField extends CtMember { + static final String javaLangString = "java.lang.String"; + + protected FieldInfo fieldInfo; + + /** + * Creates a CtField object. + * The created field must be added to a class + * with CtClass.addField(). + * An initial value of the field is specified + * by a CtField.Initializer object. + * + *

If getter and setter methods are needed, + * call CtNewMethod.getter() and + * CtNewMethod.setter(). + * + * @param type field type + * @param name field name + * @param declaring the class to which the field will be added. + * + * @see CtClass#addField(CtField) + * @see CtNewMethod#getter(String,CtField) + * @see CtNewMethod#setter(String,CtField) + * @see CtField.Initializer + */ + public CtField(CtClass type, String name, CtClass declaring) + throws CannotCompileException + { + this(Descriptor.of(type), name, declaring); + } + + /** + * Creates a copy of the given field. + * The created field must be added to a class + * with CtClass.addField(). + * An initial value of the field is specified + * by a CtField.Initializer object. + * + *

If getter and setter methods are needed, + * call CtNewMethod.getter() and + * CtNewMethod.setter(). + * + * @param src the original field + * @param declaring the class to which the field will be added. + * @see CtNewMethod#getter(String,CtField) + * @see CtNewMethod#setter(String,CtField) + * @see CtField.Initializer + */ + public CtField(CtField src, CtClass declaring) + throws CannotCompileException + { + this(src.fieldInfo.getDescriptor(), src.fieldInfo.getName(), + declaring); + java.util.ListIterator iterator + = src.fieldInfo.getAttributes().listIterator(); + FieldInfo fi = fieldInfo; + fi.setAccessFlags(src.fieldInfo.getAccessFlags()); + ConstPool cp = fi.getConstPool(); + while (iterator.hasNext()) { + AttributeInfo ainfo = (AttributeInfo)iterator.next(); + fi.addAttribute(ainfo.copy(cp, null)); + } + } + + private CtField(String typeDesc, String name, CtClass clazz) + throws CannotCompileException + { + super(clazz); + ClassFile cf = clazz.getClassFile2(); + if (cf == null) + throw new CannotCompileException("bad declaring class: " + + clazz.getName()); + + fieldInfo = new FieldInfo(cf.getConstPool(), name, typeDesc); + } + + CtField(FieldInfo fi, CtClass clazz) { + super(clazz); + fieldInfo = fi; + } + + /** + * Returns a String representation of the object. + */ + public String toString() { + return getDeclaringClass().getName() + "." + getName() + + ":" + fieldInfo.getDescriptor(); + } + + protected void extendToString(StringBuffer buffer) { + buffer.append(' '); + buffer.append(getName()); + buffer.append(' '); + buffer.append(fieldInfo.getDescriptor()); + } + + /* Javac.CtFieldWithInit overrides. + */ + protected ASTree getInitAST() { return null; } + + /* Called by CtClassType.addField(). + */ + Initializer getInit() { + ASTree tree = getInitAST(); + if (tree == null) + return null; + else + return Initializer.byExpr(tree); + } + + /** + * Compiles the given source code and creates a field. + * Examples of the source code are: + * + *

+     * "public String name;"
+     * "public int k = 3;"
+ * + *

Note that the source code ends with ';' + * (semicolon). + * + * @param src the source text. + * @param declaring the class to which the created field is added. + */ + public static CtField make(String src, CtClass declaring) + throws CannotCompileException + { + Javac compiler = new Javac(declaring); + try { + CtMember obj = compiler.compile(src); + if (obj instanceof CtField) + return (CtField)obj; // an instance of Javac.CtFieldWithInit + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + + throw new CannotCompileException("not a field"); + } + + /** + * Returns the FieldInfo representing the field in the class file. + */ + public FieldInfo getFieldInfo() { + declaringClass.checkModify(); + return fieldInfo; + } + + /** + * Returns the FieldInfo representing the field in the class + * file (read only). + * Normal applications do not need calling this method. Use + * getFieldInfo(). + * + *

The FieldInfo object obtained by this method + * is read only. Changes to this object might not be reflected + * on a class file generated by toBytecode(), + * toClass(), etc in CtClass. + * + *

This method is available even if the CtClass + * containing this field is frozen. However, if the class is + * frozen, the FieldInfo might be also pruned. + * + * @see #getFieldInfo() + * @see CtClass#isFrozen() + * @see CtClass#prune() + */ + public FieldInfo getFieldInfo2() { return fieldInfo; } + + /** + * Returns the class declaring the field. + */ + public CtClass getDeclaringClass() { + // this is redundant but for javadoc. + return super.getDeclaringClass(); + } + + /** + * Returns the name of the field. + */ + public String getName() { + return fieldInfo.getName(); + } + + /** + * Changes the name of the field. + */ + public void setName(String newName) { + declaringClass.checkModify(); + fieldInfo.setName(newName); + } + + /** + * Returns the encoded modifiers of the field. + * + * @see Modifier + */ + public int getModifiers() { + return AccessFlag.toModifier(fieldInfo.getAccessFlags()); + } + + /** + * Sets the encoded modifiers of the field. + * + * @see Modifier + */ + public void setModifiers(int mod) { + declaringClass.checkModify(); + fieldInfo.setAccessFlags(AccessFlag.of(mod)); + } + + /** + * Returns true if the class has the specified annotation class. + * + * @param clz the annotation class. + * @return true if the annotation is found, otherwise false. + * @since 3.11 + */ + public boolean hasAnnotation(Class clz) { + FieldInfo fi = getFieldInfo2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + fi.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + fi.getAttribute(AnnotationsAttribute.visibleTag); + return CtClassType.hasAnnotationType(clz, getDeclaringClass().getClassPool(), + ainfo, ainfo2); + } + + /** + * Returns the annotation if the class has the specified annotation class. + * For example, if an annotation @Author is associated + * with this field, an Author object is returned. + * The member values can be obtained by calling methods on + * the Author object. + * + * @param clz the annotation class. + * @return the annotation if found, otherwise null. + * @since 3.11 + */ + public Object getAnnotation(Class clz) throws ClassNotFoundException { + FieldInfo fi = getFieldInfo2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + fi.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + fi.getAttribute(AnnotationsAttribute.visibleTag); + return CtClassType.getAnnotationType(clz, getDeclaringClass().getClassPool(), + ainfo, ainfo2); + } + + /** + * Returns the annotations associated with this field. + * + * @return an array of annotation-type objects. + * @see #getAvailableAnnotations() + * @since 3.1 + */ + public Object[] getAnnotations() throws ClassNotFoundException { + return getAnnotations(false); + } + + /** + * Returns the annotations associated with this field. + * If any annotations are not on the classpath, they are not included + * in the returned array. + * + * @return an array of annotation-type objects. + * @see #getAnnotations() + * @since 3.3 + */ + public Object[] getAvailableAnnotations(){ + try { + return getAnnotations(true); + } + catch (ClassNotFoundException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + + private Object[] getAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { + FieldInfo fi = getFieldInfo2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + fi.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + fi.getAttribute(AnnotationsAttribute.visibleTag); + return CtClassType.toAnnotationType(ignoreNotFound, getDeclaringClass().getClassPool(), + ainfo, ainfo2); + } + + /** + * Returns the character string representing the type of the field. + * The field signature is represented by a character string + * called a field descriptor, which is defined in the JVM specification. + * If two fields have the same type, + * getSignature() returns the same string. + * + *

Note that the returned string is not the type signature + * contained in the SignatureAttirbute. It is + * a descriptor. + * + * @see javassist.bytecode.Descriptor + * @see #getGenericSignature() + */ + public String getSignature() { + return fieldInfo.getDescriptor(); + } + + /** + * Returns the generic signature of the field. + * It represents a type including type variables. + * + * @see SignatureAttribute#toFieldSignature(String) + * @since 3.17 + */ + public String getGenericSignature() { + SignatureAttribute sa + = (SignatureAttribute)fieldInfo.getAttribute(SignatureAttribute.tag); + return sa == null ? null : sa.getSignature(); + } + + /** + * Set the generic signature of the field. + * It represents a type including type variables. + * See {@link javassist.CtClass#setGenericSignature(String)} + * for a code sample. + * + * @param sig a new generic signature. + * @see javassist.bytecode.SignatureAttribute.ObjectType#encode() + * @since 3.17 + */ + public void setGenericSignature(String sig) { + declaringClass.checkModify(); + fieldInfo.addAttribute(new SignatureAttribute(fieldInfo.getConstPool(), sig)); + } + + /** + * Returns the type of the field. + */ + public CtClass getType() throws NotFoundException { + return Descriptor.toCtClass(fieldInfo.getDescriptor(), + declaringClass.getClassPool()); + } + + /** + * Sets the type of the field. + */ + public void setType(CtClass clazz) { + declaringClass.checkModify(); + fieldInfo.setDescriptor(Descriptor.of(clazz)); + } + + /** + * Returns the value of this field if it is a constant field. + * This method works only if the field type is a primitive type + * or String type. Otherwise, it returns null. + * A constant field is static and final. + * + * @return a Integer, Long, Float, + * Double, Boolean, + * or String object + * representing the constant value. + * null if it is not a constant field + * or if the field type is not a primitive type + * or String. + */ + public Object getConstantValue() { + // When this method is modified, + // see also getConstantFieldValue() in TypeChecker. + + int index = fieldInfo.getConstantValue(); + if (index == 0) + return null; + + ConstPool cp = fieldInfo.getConstPool(); + switch (cp.getTag(index)) { + case ConstPool.CONST_Long : + return new Long(cp.getLongInfo(index)); + case ConstPool.CONST_Float : + return new Float(cp.getFloatInfo(index)); + case ConstPool.CONST_Double : + return new Double(cp.getDoubleInfo(index)); + case ConstPool.CONST_Integer : + int value = cp.getIntegerInfo(index); + // "Z" means boolean type. + if ("Z".equals(fieldInfo.getDescriptor())) + return new Boolean(value != 0); + else + return new Integer(value); + case ConstPool.CONST_String : + return cp.getStringInfo(index); + default : + throw new RuntimeException("bad tag: " + cp.getTag(index) + + " at " + index); + } + } + + /** + * Obtains an attribute with the given name. + * If that attribute is not found in the class file, this + * method returns null. + * + *

Note that an attribute is a data block specified by + * the class file format. + * See {@link javassist.bytecode.AttributeInfo}. + * + * @param name attribute name + */ + public byte[] getAttribute(String name) { + AttributeInfo ai = fieldInfo.getAttribute(name); + if (ai == null) + return null; + else + return ai.get(); + } + + /** + * Adds an attribute. The attribute is saved in the class file. + * + *

Note that an attribute is a data block specified by + * the class file format. + * See {@link javassist.bytecode.AttributeInfo}. + * + * @param name attribute name + * @param data attribute value + */ + public void setAttribute(String name, byte[] data) { + declaringClass.checkModify(); + fieldInfo.addAttribute(new AttributeInfo(fieldInfo.getConstPool(), + name, data)); + } + + // inner classes + + /** + * Instances of this class specify how to initialize a field. + * Initializer is passed to + * CtClass.addField() with a CtField. + * + *

This class cannot be instantiated with the new operator. + * Factory methods such as byParameter() and + * byNew + * must be used for the instantiation. They create a new instance with + * the given parameters and return it. + * + * @see CtClass#addField(CtField,CtField.Initializer) + */ + public static abstract class Initializer { + /** + * Makes an initializer that assigns a constant integer value. + * The field must be integer, short, char, or byte type. + */ + public static Initializer constant(int i) { + return new IntInitializer(i); + } + + /** + * Makes an initializer that assigns a constant boolean value. + * The field must be boolean type. + */ + public static Initializer constant(boolean b) { + return new IntInitializer(b ? 1 : 0); + } + + /** + * Makes an initializer that assigns a constant long value. + * The field must be long type. + */ + public static Initializer constant(long l) { + return new LongInitializer(l); + } + + /** + * Makes an initializer that assigns a constant float value. + * The field must be float type. + */ + public static Initializer constant(float l) { + return new FloatInitializer(l); + } + + /** + * Makes an initializer that assigns a constant double value. + * The field must be double type. + */ + public static Initializer constant(double d) { + return new DoubleInitializer(d); + } + + /** + * Makes an initializer that assigns a constant string value. + * The field must be java.lang.String type. + */ + public static Initializer constant(String s) { + return new StringInitializer(s); + } + + /** + * Makes an initializer using a constructor parameter. + * + *

The initial value is the + * N-th parameter given to the constructor of the object including + * the field. If the constructor takes less than N parameters, + * the field is not initialized. + * If the field is static, it is never initialized. + * + * @param nth the n-th (>= 0) parameter is used as + * the initial value. + * If nth is 0, then the first parameter is + * used. + */ + public static Initializer byParameter(int nth) { + ParamInitializer i = new ParamInitializer(); + i.nthParam = nth; + return i; + } + + /** + * Makes an initializer creating a new object. + * + *

This initializer creates a new object and uses it as the initial + * value of the field. The constructor of the created object receives + * the parameter: + * + *

Object obj - the object including the field. + * + *

If the initialized field is static, then the constructor does + * not receive any parameters. + * + * @param objectType the class instantiated for the initial value. + */ + public static Initializer byNew(CtClass objectType) { + NewInitializer i = new NewInitializer(); + i.objectType = objectType; + i.stringParams = null; + i.withConstructorParams = false; + return i; + } + + /** + * Makes an initializer creating a new object. + * + *

This initializer creates a new object and uses it as the initial + * value of the field. The constructor of the created object receives + * the parameters: + * + *

Object obj - the object including the field.
+ * String[] strs - the character strings specified + * by stringParams
+ * + *

If the initialized field is static, then the constructor + * receives only strs. + * + * @param objectType the class instantiated for the initial value. + * @param stringParams the array of strings passed to the + * constructor. + */ + public static Initializer byNew(CtClass objectType, + String[] stringParams) { + NewInitializer i = new NewInitializer(); + i.objectType = objectType; + i.stringParams = stringParams; + i.withConstructorParams = false; + return i; + } + + /** + * Makes an initializer creating a new object. + * + *

This initializer creates a new object and uses it as the initial + * value of the field. The constructor of the created object receives + * the parameters: + * + *

Object obj - the object including the field.
+ * Object[] args - the parameters passed to the + * constructor of the object including the + * filed. + * + *

If the initialized field is static, then the constructor does + * not receive any parameters. + * + * @param objectType the class instantiated for the initial value. + * + * @see javassist.CtField.Initializer#byNewArray(CtClass,int) + * @see javassist.CtField.Initializer#byNewArray(CtClass,int[]) + */ + public static Initializer byNewWithParams(CtClass objectType) { + NewInitializer i = new NewInitializer(); + i.objectType = objectType; + i.stringParams = null; + i.withConstructorParams = true; + return i; + } + + /** + * Makes an initializer creating a new object. + * + *

This initializer creates a new object and uses it as the initial + * value of the field. The constructor of the created object receives + * the parameters: + * + *

Object obj - the object including the field.
+ * String[] strs - the character strings specified + * by stringParams
+ * Object[] args - the parameters passed to the + * constructor of the object including the + * filed. + * + *

If the initialized field is static, then the constructor receives + * only strs. + * + * @param objectType the class instantiated for the initial value. + * @param stringParams the array of strings passed to the + * constructor. + */ + public static Initializer byNewWithParams(CtClass objectType, + String[] stringParams) { + NewInitializer i = new NewInitializer(); + i.objectType = objectType; + i.stringParams = stringParams; + i.withConstructorParams = true; + return i; + } + + /** + * Makes an initializer calling a static method. + * + *

This initializer calls a static method and uses the returned + * value as the initial value of the field. + * The called method receives the parameters: + * + *

Object obj - the object including the field. + * + *

If the initialized field is static, then the method does + * not receive any parameters. + * + *

The type of the returned value must be the same as the field + * type. + * + * @param methodClass the class that the static method is + * declared in. + * @param methodName the name of the satic method. + */ + public static Initializer byCall(CtClass methodClass, + String methodName) { + MethodInitializer i = new MethodInitializer(); + i.objectType = methodClass; + i.methodName = methodName; + i.stringParams = null; + i.withConstructorParams = false; + return i; + } + + /** + * Makes an initializer calling a static method. + * + *

This initializer calls a static method and uses the returned + * value as the initial value of the field. The called method + * receives the parameters: + * + *

Object obj - the object including the field.
+ * String[] strs - the character strings specified + * by stringParams
+ * + *

If the initialized field is static, then the method + * receive only strs. + * + *

The type of the returned value must be the same as the field + * type. + * + * @param methodClass the class that the static method is + * declared in. + * @param methodName the name of the satic method. + * @param stringParams the array of strings passed to the + * static method. + */ + public static Initializer byCall(CtClass methodClass, + String methodName, + String[] stringParams) { + MethodInitializer i = new MethodInitializer(); + i.objectType = methodClass; + i.methodName = methodName; + i.stringParams = stringParams; + i.withConstructorParams = false; + return i; + } + + /** + * Makes an initializer calling a static method. + * + *

This initializer calls a static method and uses the returned + * value as the initial value of the field. The called method + * receives the parameters: + * + *

Object obj - the object including the field.
+ * Object[] args - the parameters passed to the + * constructor of the object including the + * filed. + * + *

If the initialized field is static, then the method does + * not receive any parameters. + * + *

The type of the returned value must be the same as the field + * type. + * + * @param methodClass the class that the static method is + * declared in. + * @param methodName the name of the satic method. + */ + public static Initializer byCallWithParams(CtClass methodClass, + String methodName) { + MethodInitializer i = new MethodInitializer(); + i.objectType = methodClass; + i.methodName = methodName; + i.stringParams = null; + i.withConstructorParams = true; + return i; + } + + /** + * Makes an initializer calling a static method. + * + *

This initializer calls a static method and uses the returned + * value as the initial value of the field. The called method + * receives the parameters: + * + *

Object obj - the object including the field.
+ * String[] strs - the character strings specified + * by stringParams
+ * Object[] args - the parameters passed to the + * constructor of the object including the + * filed. + * + *

If the initialized field is static, then the method + * receive only strs. + * + *

The type of the returned value must be the same as the field + * type. + * + * @param methodClass the class that the static method is + * declared in. + * @param methodName the name of the satic method. + * @param stringParams the array of strings passed to the + * static method. + */ + public static Initializer byCallWithParams(CtClass methodClass, + String methodName, String[] stringParams) { + MethodInitializer i = new MethodInitializer(); + i.objectType = methodClass; + i.methodName = methodName; + i.stringParams = stringParams; + i.withConstructorParams = true; + return i; + } + + /** + * Makes an initializer creating a new array. + * + * @param type the type of the array. + * @param size the size of the array. + * @throws NotFoundException if the type of the array components + * is not found. + */ + public static Initializer byNewArray(CtClass type, int size) + throws NotFoundException + { + return new ArrayInitializer(type.getComponentType(), size); + } + + /** + * Makes an initializer creating a new multi-dimensional array. + * + * @param type the type of the array. + * @param sizes an int array of the size in every + * dimension. + * The first element is the size in the first + * dimension. The second is in the second, etc. + */ + public static Initializer byNewArray(CtClass type, int[] sizes) { + return new MultiArrayInitializer(type, sizes); + } + + /** + * Makes an initializer. + * + * @param source initializer expression. + */ + public static Initializer byExpr(String source) { + return new CodeInitializer(source); + } + + static Initializer byExpr(ASTree source) { + return new PtreeInitializer(source); + } + + // Check whether this initializer is valid for the field type. + // If it is invaild, this method throws an exception. + void check(String desc) throws CannotCompileException {} + + // produce codes for initialization + abstract int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException; + + // produce codes for initialization + abstract int compileIfStatic(CtClass type, String name, + Bytecode code, Javac drv) throws CannotCompileException; + + // returns the index of CONSTANT_Integer_info etc + // if the value is constant. Otherwise, 0. + int getConstantValue(ConstPool cp, CtClass type) { return 0; } + } + + static abstract class CodeInitializer0 extends Initializer { + abstract void compileExpr(Javac drv) throws CompileError; + + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + try { + code.addAload(0); + compileExpr(drv); + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return code.getMaxStack(); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + } + + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + try { + compileExpr(drv); + code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); + return code.getMaxStack(); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + } + + int getConstantValue2(ConstPool cp, CtClass type, ASTree tree) { + if (type.isPrimitive()) { + if (tree instanceof IntConst) { + long value = ((IntConst)tree).get(); + if (type == CtClass.doubleType) + return cp.addDoubleInfo((double)value); + else if (type == CtClass.floatType) + return cp.addFloatInfo((float)value); + else if (type == CtClass.longType) + return cp.addLongInfo(value); + else if (type != CtClass.voidType) + return cp.addIntegerInfo((int)value); + } + else if (tree instanceof DoubleConst) { + double value = ((DoubleConst)tree).get(); + if (type == CtClass.floatType) + return cp.addFloatInfo((float)value); + else if (type == CtClass.doubleType) + return cp.addDoubleInfo(value); + } + } + else if (tree instanceof StringL + && type.getName().equals(javaLangString)) + return cp.addStringInfo(((StringL)tree).get()); + + return 0; + } + } + + static class CodeInitializer extends CodeInitializer0 { + private String expression; + + CodeInitializer(String expr) { expression = expr; } + + void compileExpr(Javac drv) throws CompileError { + drv.compileExpr(expression); + } + + int getConstantValue(ConstPool cp, CtClass type) { + try { + ASTree t = Javac.parseExpr(expression, new SymbolTable()); + return getConstantValue2(cp, type, t); + } + catch (CompileError e) { + return 0; + } + } + } + + static class PtreeInitializer extends CodeInitializer0 { + private ASTree expression; + + PtreeInitializer(ASTree expr) { expression = expr; } + + void compileExpr(Javac drv) throws CompileError { + drv.compileExpr(expression); + } + + int getConstantValue(ConstPool cp, CtClass type) { + return getConstantValue2(cp, type, expression); + } + } + + /** + * A field initialized with a parameter passed to the constructor + * of the class containing that field. + */ + static class ParamInitializer extends Initializer { + int nthParam; + + ParamInitializer() {} + + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + if (parameters != null && nthParam < parameters.length) { + code.addAload(0); + int nth = nthParamToLocal(nthParam, parameters, false); + int s = code.addLoad(nth, type) + 1; + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return s; // stack size + } + else + return 0; // do not initialize + } + + /** + * Computes the index of the local variable that the n-th parameter + * is assigned to. + * + * @param nth n-th parameter + * @param params list of parameter types + * @param isStatic true if the method is static. + */ + static int nthParamToLocal(int nth, CtClass[] params, + boolean isStatic) { + CtClass longType = CtClass.longType; + CtClass doubleType = CtClass.doubleType; + int k; + if (isStatic) + k = 0; + else + k = 1; // 0 is THIS. + + for (int i = 0; i < nth; ++i) { + CtClass type = params[i]; + if (type == longType || type == doubleType) + k += 2; + else + ++k; + } + + return k; + } + + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + return 0; + } + } + + /** + * A field initialized with an object created by the new operator. + */ + static class NewInitializer extends Initializer { + CtClass objectType; + String[] stringParams; + boolean withConstructorParams; + + NewInitializer() {} + + /** + * Produces codes in which a new object is created and assigned to + * the field as the initial value. + */ + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + int stacksize; + + code.addAload(0); + code.addNew(objectType); + code.add(Bytecode.DUP); + code.addAload(0); + + if (stringParams == null) + stacksize = 4; + else + stacksize = compileStringParameter(code) + 4; + + if (withConstructorParams) + stacksize += CtNewWrappedMethod.compileParameterList(code, + parameters, 1); + + code.addInvokespecial(objectType, "", getDescriptor()); + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return stacksize; + } + + private String getDescriptor() { + final String desc3 + = "(Ljava/lang/Object;[Ljava/lang/String;[Ljava/lang/Object;)V"; + + if (stringParams == null) + if (withConstructorParams) + return "(Ljava/lang/Object;[Ljava/lang/Object;)V"; + else + return "(Ljava/lang/Object;)V"; + else + if (withConstructorParams) + return desc3; + else + return "(Ljava/lang/Object;[Ljava/lang/String;)V"; + } + + /** + * Produces codes for a static field. + */ + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + String desc; + + code.addNew(objectType); + code.add(Bytecode.DUP); + + int stacksize = 2; + if (stringParams == null) + desc = "()V"; + else { + desc = "([Ljava/lang/String;)V"; + stacksize += compileStringParameter(code); + } + + code.addInvokespecial(objectType, "", desc); + code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); + return stacksize; + } + + protected final int compileStringParameter(Bytecode code) + throws CannotCompileException + { + int nparam = stringParams.length; + code.addIconst(nparam); + code.addAnewarray(javaLangString); + for (int j = 0; j < nparam; ++j) { + code.add(Bytecode.DUP); // dup + code.addIconst(j); // iconst_ + code.addLdc(stringParams[j]); // ldc ... + code.add(Bytecode.AASTORE); // aastore + } + + return 4; + } + + } + + /** + * A field initialized with the result of a static method call. + */ + static class MethodInitializer extends NewInitializer { + String methodName; + // the method class is specified by objectType. + + MethodInitializer() {} + + /** + * Produces codes in which a new object is created and assigned to + * the field as the initial value. + */ + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + int stacksize; + + code.addAload(0); + code.addAload(0); + + if (stringParams == null) + stacksize = 2; + else + stacksize = compileStringParameter(code) + 2; + + if (withConstructorParams) + stacksize += CtNewWrappedMethod.compileParameterList(code, + parameters, 1); + + String typeDesc = Descriptor.of(type); + String mDesc = getDescriptor() + typeDesc; + code.addInvokestatic(objectType, methodName, mDesc); + code.addPutfield(Bytecode.THIS, name, typeDesc); + return stacksize; + } + + private String getDescriptor() { + final String desc3 + = "(Ljava/lang/Object;[Ljava/lang/String;[Ljava/lang/Object;)"; + + if (stringParams == null) + if (withConstructorParams) + return "(Ljava/lang/Object;[Ljava/lang/Object;)"; + else + return "(Ljava/lang/Object;)"; + else + if (withConstructorParams) + return desc3; + else + return "(Ljava/lang/Object;[Ljava/lang/String;)"; + } + + /** + * Produces codes for a static field. + */ + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + String desc; + + int stacksize = 1; + if (stringParams == null) + desc = "()"; + else { + desc = "([Ljava/lang/String;)"; + stacksize += compileStringParameter(code); + } + + String typeDesc = Descriptor.of(type); + code.addInvokestatic(objectType, methodName, desc + typeDesc); + code.addPutstatic(Bytecode.THIS, name, typeDesc); + return stacksize; + } + } + + static class IntInitializer extends Initializer { + int value; + + IntInitializer(int v) { value = v; } + + void check(String desc) throws CannotCompileException { + char c = desc.charAt(0); + if (c != 'I' && c != 'S' && c != 'B' && c != 'C' && c != 'Z') + throw new CannotCompileException("type mismatch"); + } + + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + code.addAload(0); + code.addIconst(value); + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return 2; // stack size + } + + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + code.addIconst(value); + code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); + return 1; // stack size + } + + int getConstantValue(ConstPool cp, CtClass type) { + return cp.addIntegerInfo(value); + } + } + + static class LongInitializer extends Initializer { + long value; + + LongInitializer(long v) { value = v; } + + void check(String desc) throws CannotCompileException { + if (!desc.equals("J")) + throw new CannotCompileException("type mismatch"); + } + + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + code.addAload(0); + code.addLdc2w(value); + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return 3; // stack size + } + + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + code.addLdc2w(value); + code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); + return 2; // stack size + } + + int getConstantValue(ConstPool cp, CtClass type) { + if (type == CtClass.longType) + return cp.addLongInfo(value); + else + return 0; + } + } + + static class FloatInitializer extends Initializer { + float value; + + FloatInitializer(float v) { value = v; } + + void check(String desc) throws CannotCompileException { + if (!desc.equals("F")) + throw new CannotCompileException("type mismatch"); + } + + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + code.addAload(0); + code.addFconst(value); + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return 3; // stack size + } + + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + code.addFconst(value); + code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); + return 2; // stack size + } + + int getConstantValue(ConstPool cp, CtClass type) { + if (type == CtClass.floatType) + return cp.addFloatInfo(value); + else + return 0; + } + } + + static class DoubleInitializer extends Initializer { + double value; + + DoubleInitializer(double v) { value = v; } + + void check(String desc) throws CannotCompileException { + if (!desc.equals("D")) + throw new CannotCompileException("type mismatch"); + } + + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + code.addAload(0); + code.addLdc2w(value); + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return 3; // stack size + } + + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + code.addLdc2w(value); + code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); + return 2; // stack size + } + + int getConstantValue(ConstPool cp, CtClass type) { + if (type == CtClass.doubleType) + return cp.addDoubleInfo(value); + else + return 0; + } + } + + static class StringInitializer extends Initializer { + String value; + + StringInitializer(String v) { value = v; } + + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + code.addAload(0); + code.addLdc(value); + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return 2; // stack size + } + + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + code.addLdc(value); + code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); + return 1; // stack size + } + + int getConstantValue(ConstPool cp, CtClass type) { + if (type.getName().equals(javaLangString)) + return cp.addStringInfo(value); + else + return 0; + } + } + + static class ArrayInitializer extends Initializer { + CtClass type; + int size; + + ArrayInitializer(CtClass t, int s) { type = t; size = s; } + + private void addNewarray(Bytecode code) { + if (type.isPrimitive()) + code.addNewarray(((CtPrimitiveType)type).getArrayType(), + size); + else + code.addAnewarray(type, size); + } + + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + code.addAload(0); + addNewarray(code); + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return 2; // stack size + } + + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + addNewarray(code); + code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); + return 1; // stack size + } + } + + static class MultiArrayInitializer extends Initializer { + CtClass type; + int[] dim; + + MultiArrayInitializer(CtClass t, int[] d) { type = t; dim = d; } + + void check(String desc) throws CannotCompileException { + if (desc.charAt(0) != '[') + throw new CannotCompileException("type mismatch"); + } + + int compile(CtClass type, String name, Bytecode code, + CtClass[] parameters, Javac drv) + throws CannotCompileException + { + code.addAload(0); + int s = code.addMultiNewarray(type, dim); + code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); + return s + 1; // stack size + } + + int compileIfStatic(CtClass type, String name, Bytecode code, + Javac drv) throws CannotCompileException + { + int s = code.addMultiNewarray(type, dim); + code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); + return s; // stack size + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtMember.java b/src/main/java/com/wenshuo/agent/javassist/CtMember.java new file mode 100644 index 0000000..2e83f97 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtMember.java @@ -0,0 +1,319 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +/** + * An instance of CtMember represents a field, a constructor, + * or a method. + */ +public abstract class CtMember { + CtMember next; // for internal use + protected CtClass declaringClass; + + /* Make a circular link of CtMembers declared in the + * same class so that they are garbage-collected together + * at the same time. + */ + static class Cache extends CtMember { + protected void extendToString(StringBuffer buffer) {} + public boolean hasAnnotation(Class clz) { return false; } + public Object getAnnotation(Class clz) + throws ClassNotFoundException { return null; } + public Object[] getAnnotations() + throws ClassNotFoundException { return null; } + public byte[] getAttribute(String name) { return null; } + public Object[] getAvailableAnnotations() { return null; } + public int getModifiers() { return 0; } + public String getName() { return null; } + public String getSignature() { return null; } + public void setAttribute(String name, byte[] data) {} + public void setModifiers(int mod) {} + public String getGenericSignature() { return null; } + public void setGenericSignature(String sig) {} + + private CtMember methodTail; + private CtMember consTail; // constructor tail + private CtMember fieldTail; + + Cache(CtClassType decl) { + super(decl); + methodTail = this; + consTail = this; + fieldTail = this; + fieldTail.next = this; + } + + CtMember methodHead() { return this; } + CtMember lastMethod() { return methodTail; } + CtMember consHead() { return methodTail; } // may include a static initializer + CtMember lastCons() { return consTail; } + CtMember fieldHead() { return consTail; } + CtMember lastField() { return fieldTail; } + + void addMethod(CtMember method) { + method.next = methodTail.next; + methodTail.next = method; + if (methodTail == consTail) { + consTail = method; + if (methodTail == fieldTail) + fieldTail = method; + } + + methodTail = method; + } + + /* Both constructors and a class initializer. + */ + void addConstructor(CtMember cons) { + cons.next = consTail.next; + consTail.next = cons; + if (consTail == fieldTail) + fieldTail = cons; + + consTail = cons; + } + + void addField(CtMember field) { + field.next = this; // or fieldTail.next + fieldTail.next = field; + fieldTail = field; + } + + static int count(CtMember head, CtMember tail) { + int n = 0; + while (head != tail) { + n++; + head = head.next; + } + + return n; + } + + void remove(CtMember mem) { + CtMember m = this; + CtMember node; + while ((node = m.next) != this) { + if (node == mem) { + m.next = node.next; + if (node == methodTail) + methodTail = m; + + if (node == consTail) + consTail = m; + + if (node == fieldTail) + fieldTail = m; + + break; + } + else + m = m.next; + } + } + } + + protected CtMember(CtClass clazz) { + declaringClass = clazz; + next = null; + } + + final CtMember next() { return next; } + + /** + * This method is invoked when setName() or replaceClassName() + * in CtClass is called. + * + * @see CtMethod#nameReplaced() + */ + void nameReplaced() {} + + public String toString() { + StringBuffer buffer = new StringBuffer(getClass().getName()); + buffer.append("@"); + buffer.append(Integer.toHexString(hashCode())); + buffer.append("["); + buffer.append(Modifier.toString(getModifiers())); + extendToString(buffer); + buffer.append("]"); + return buffer.toString(); + } + + /** + * Invoked by {@link #toString()} to add to the buffer and provide the + * complete value. Subclasses should invoke this method, adding a + * space before each token. The modifiers for the member are + * provided first; subclasses should provide additional data such + * as return type, field or method name, etc. + */ + protected abstract void extendToString(StringBuffer buffer); + + /** + * Returns the class that declares this member. + */ + public CtClass getDeclaringClass() { return declaringClass; } + + /** + * Returns true if this member is accessible from the given class. + */ + public boolean visibleFrom(CtClass clazz) { + int mod = getModifiers(); + if (Modifier.isPublic(mod)) + return true; + else if (Modifier.isPrivate(mod)) + return clazz == declaringClass; + else { // package or protected + String declName = declaringClass.getPackageName(); + String fromName = clazz.getPackageName(); + boolean visible; + if (declName == null) + visible = fromName == null; + else + visible = declName.equals(fromName); + + if (!visible && Modifier.isProtected(mod)) + return clazz.subclassOf(declaringClass); + + return visible; + } + } + + /** + * Obtains the modifiers of the member. + * + * @return modifiers encoded with + * javassist.Modifier. + * @see Modifier + */ + public abstract int getModifiers(); + + /** + * Sets the encoded modifiers of the member. + * + * @see Modifier + */ + public abstract void setModifiers(int mod); + + /** + * Returns true if the class has the specified annotation class. + * + * @param clz the annotation class. + * @return true if the annotation is found, otherwise false. + * @since 3.11 + */ + public abstract boolean hasAnnotation(Class clz); + + /** + * Returns the annotation if the class has the specified annotation class. + * For example, if an annotation @Author is associated + * with this member, an Author object is returned. + * The member values can be obtained by calling methods on + * the Author object. + * + * @param clz the annotation class. + * @return the annotation if found, otherwise null. + * @since 3.11 + */ + public abstract Object getAnnotation(Class clz) throws ClassNotFoundException; + + /** + * Returns the annotations associated with this member. + * For example, if an annotation @Author is associated + * with this member, the returned array contains an Author + * object. The member values can be obtained by calling methods on + * the Author object. + * + * @return an array of annotation-type objects. + * @see CtClass#getAnnotations() + */ + public abstract Object[] getAnnotations() throws ClassNotFoundException; + + /** + * Returns the annotations associated with this member. + * This method is equivalent to getAnnotations() + * except that, if any annotations are not on the classpath, + * they are not included in the returned array. + * + * @return an array of annotation-type objects. + * @see #getAnnotations() + * @see CtClass#getAvailableAnnotations() + * @since 3.3 + */ + public abstract Object[] getAvailableAnnotations(); + + /** + * Obtains the name of the member. + * + *

As for constructor names, see getName() + * in CtConstructor. + * + * @see CtConstructor#getName() + */ + public abstract String getName(); + + /** + * Returns the character string representing the signature of the member. + * If two members have the same signature (parameter types etc.), + * getSignature() returns the same string. + */ + public abstract String getSignature(); + + /** + * Returns the generic signature of the member. + * + * @see javassist.bytecode.SignatureAttribute#toFieldSignature(String) + * @see javassist.bytecode.SignatureAttribute#toMethodSignature(String) + * @see CtClass#getGenericSignature() + * @since 3.17 + */ + public abstract String getGenericSignature(); + + /** + * Sets the generic signature of the member. + * + * @param sig a new generic signature. + * @see javassist.bytecode.SignatureAttribute.ObjectType#encode() + * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode() + * @see CtClass#setGenericSignature(String) + * @since 3.17 + */ + public abstract void setGenericSignature(String sig); + + /** + * Obtains a user-defined attribute with the given name. + * If that attribute is not found in the class file, this + * method returns null. + * + *

Note that an attribute is a data block specified by + * the class file format. + * See {@link javassist.bytecode.AttributeInfo}. + * + * @param name attribute name + */ + public abstract byte[] getAttribute(String name); + + /** + * Adds a user-defined attribute. The attribute is saved in the class file. + * + *

Note that an attribute is a data block specified by + * the class file format. + * See {@link javassist.bytecode.AttributeInfo}. + * + * @param name attribute name + * @param data attribute value + */ + public abstract void setAttribute(String name, byte[] data); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtMethod.java b/src/main/java/com/wenshuo/agent/javassist/CtMethod.java new file mode 100644 index 0000000..42d9f22 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtMethod.java @@ -0,0 +1,438 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.*; + +/** + * An instance of CtMethod represents a method. + * + *

See the super class CtBehavior since + * a number of useful methods are in CtBehavior. + * A number of useful factory methods are in CtNewMethod. + * + * @see CtClass#getDeclaredMethods() + * @see CtNewMethod + */ +public final class CtMethod extends CtBehavior { + protected String cachedStringRep; + + /** + * @see #make(MethodInfo minfo, CtClass declaring) + */ + CtMethod(MethodInfo minfo, CtClass declaring) { + super(declaring, minfo); + cachedStringRep = null; + } + + /** + * Creates a public abstract method. The created method must be + * added to a class with CtClass.addMethod(). + * + * @param declaring the class to which the created method is added. + * @param returnType the type of the returned value + * @param mname the method name + * @param parameters a list of the parameter types + * + * @see CtClass#addMethod(CtMethod) + */ + public CtMethod(CtClass returnType, String mname, + CtClass[] parameters, CtClass declaring) { + this(null, declaring); + ConstPool cp = declaring.getClassFile2().getConstPool(); + String desc = Descriptor.ofMethod(returnType, parameters); + methodInfo = new MethodInfo(cp, mname, desc); + setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT); + } + + /** + * Creates a copy of a CtMethod object. + * The created method must be + * added to a class with CtClass.addMethod(). + * + *

All occurrences of class names in the created method + * are replaced with names specified by + * map if map is not null. + * + *

For example, suppose that a method at() is as + * follows: + * + *

+     * public X at(int i) {
+     *     return (X)super.elementAt(i);
+     * }
+ * + *

(X is a class name.) If map substitutes + * String for X, then the created method is: + * + *

+     * public String at(int i) {
+     *     return (String)super.elementAt(i);
+     * }
+ * + *

By default, all the occurrences of the names of the class + * declaring at() and the superclass are replaced + * with the name of the class and the superclass that the + * created method is added to. + * This is done whichever map is null or not. + * To prevent this replacement, call ClassMap.fix() + * or put() to explicitly specify replacement. + * + *

Note: if the .class notation (for example, + * String.class) is included in an expression, the + * Javac compiler may produce a helper method. + * Since this constructor never + * copies this helper method, the programmers have the responsiblity of + * copying it. Otherwise, use Class.forName() in the + * expression. + * + * @param src the source method. + * @param declaring the class to which the created method is added. + * @param map the hashtable associating original class names + * with substituted names. + * It can be null. + * + * @see CtClass#addMethod(CtMethod) + * @see ClassMap#fix(String) + */ + public CtMethod(CtMethod src, CtClass declaring, ClassMap map) + throws CannotCompileException + { + this(null, declaring); + copy(src, false, map); + } + + /** + * Compiles the given source code and creates a method. + * This method simply delegates to make() in + * CtNewMethod. See it for more details. + * CtNewMethod has a number of useful factory methods. + * + * @param src the source text. + * @param declaring the class to which the created method is added. + * @see CtNewMethod#make(String, CtClass) + */ + public static CtMethod make(String src, CtClass declaring) + throws CannotCompileException + { + return CtNewMethod.make(src, declaring); + } + + /** + * Creates a method from a MethodInfo object. + * + * @param declaring the class declaring the method. + * @throws CannotCompileException if the the MethodInfo + * object and the declaring class have different + * ConstPool objects + * @since 3.6 + */ + public static CtMethod make(MethodInfo minfo, CtClass declaring) + throws CannotCompileException + { + if (declaring.getClassFile2().getConstPool() != minfo.getConstPool()) + throw new CannotCompileException("bad declaring class"); + + return new CtMethod(minfo, declaring); + } + + /** + * Returns a hash code value for the method. + * If two methods have the same name and signature, then + * the hash codes for the two methods are equal. + */ + public int hashCode() { + return getStringRep().hashCode(); + } + + /** + * This method is invoked when setName() or replaceClassName() + * in CtClass is called. + */ + void nameReplaced() { + cachedStringRep = null; + } + + /* This method is also called by CtClassType.getMethods0(). + */ + final String getStringRep() { + if (cachedStringRep == null) + cachedStringRep = methodInfo.getName() + + Descriptor.getParamDescriptor(methodInfo.getDescriptor()); + + return cachedStringRep; + } + + /** + * Indicates whether obj has the same name and the + * same signature as this method. + */ + public boolean equals(Object obj) { + return obj != null && obj instanceof CtMethod + && ((CtMethod)obj).getStringRep().equals(getStringRep()); + } + + /** + * Returns the method name followed by parameter types + * such as javassist.CtMethod.setBody(String). + * + * @since 3.5 + */ + public String getLongName() { + return getDeclaringClass().getName() + "." + + getName() + Descriptor.toString(getSignature()); + } + + /** + * Obtains the name of this method. + */ + public String getName() { + return methodInfo.getName(); + } + + /** + * Changes the name of this method. + */ + public void setName(String newname) { + declaringClass.checkModify(); + methodInfo.setName(newname); + } + + /** + * Obtains the type of the returned value. + */ + public CtClass getReturnType() throws NotFoundException { + return getReturnType0(); + } + + /** + * Returns true if the method body is empty, that is, {}. + * It also returns true if the method is an abstract method. + */ + public boolean isEmpty() { + CodeAttribute ca = getMethodInfo2().getCodeAttribute(); + if (ca == null) // abstract or native + return (getModifiers() & Modifier.ABSTRACT) != 0; + + CodeIterator it = ca.iterator(); + try { + return it.hasNext() && it.byteAt(it.next()) == Opcode.RETURN + && !it.hasNext(); + } + catch (BadBytecode e) {} + return false; + } + + /** + * Copies a method body from another method. + * If this method is abstract, the abstract modifier is removed + * after the method body is copied. + * + *

All occurrences of the class names in the copied method body + * are replaced with the names specified by + * map if map is not null. + * + * @param src the method that the body is copied from. + * @param map the hashtable associating original class names + * with substituted names. + * It can be null. + */ + public void setBody(CtMethod src, ClassMap map) + throws CannotCompileException + { + setBody0(src.declaringClass, src.methodInfo, + declaringClass, methodInfo, map); + } + + /** + * Replace a method body with a new method body wrapping the + * given method. + * + * @param mbody the wrapped method + * @param constParam the constant parameter given to + * the wrapped method + * (maybe null). + * + * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass) + */ + public void setWrappedBody(CtMethod mbody, ConstParameter constParam) + throws CannotCompileException + { + declaringClass.checkModify(); + + CtClass clazz = getDeclaringClass(); + CtClass[] params; + CtClass retType; + try { + params = getParameterTypes(); + retType = getReturnType(); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + + Bytecode code = CtNewWrappedMethod.makeBody(clazz, + clazz.getClassFile2(), + mbody, + params, retType, + constParam); + CodeAttribute cattr = code.toCodeAttribute(); + methodInfo.setCodeAttribute(cattr); + methodInfo.setAccessFlags(methodInfo.getAccessFlags() + & ~AccessFlag.ABSTRACT); + // rebuilding a stack map table is not needed. + } + + // inner classes + + /** + * Instances of this class represent a constant parameter. + * They are used to specify the parameter given to the methods + * created by CtNewMethod.wrapped(). + * + * @see CtMethod#setWrappedBody(CtMethod,CtMethod.ConstParameter) + * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass) + * @see CtNewConstructor#make(CtClass[],CtClass[],int,CtMethod,CtMethod.ConstParameter,CtClass) + */ + public static class ConstParameter { + /** + * Makes an integer constant. + * + * @param i the constant value. + */ + public static ConstParameter integer(int i) { + return new IntConstParameter(i); + } + + /** + * Makes a long integer constant. + * + * @param i the constant value. + */ + public static ConstParameter integer(long i) { + return new LongConstParameter(i); + } + + /** + * Makes an String constant. + * + * @param s the constant value. + */ + public static ConstParameter string(String s) { + return new StringConstParameter(s); + } + + ConstParameter() {} + + /** + * @return the size of the stack consumption. + */ + int compile(Bytecode code) throws CannotCompileException { + return 0; + } + + String descriptor() { + return defaultDescriptor(); + } + + /** + * @see CtNewWrappedMethod + */ + static String defaultDescriptor() { + return "([Ljava/lang/Object;)Ljava/lang/Object;"; + } + + /** + * Returns the descriptor for constructors. + * + * @see CtNewWrappedConstructor + */ + String constDescriptor() { + return defaultConstDescriptor(); + } + + /** + * Returns the default descriptor for constructors. + */ + static String defaultConstDescriptor() { + return "([Ljava/lang/Object;)V"; + } + } + + static class IntConstParameter extends ConstParameter { + int param; + + IntConstParameter(int i) { + param = i; + } + + int compile(Bytecode code) throws CannotCompileException { + code.addIconst(param); + return 1; + } + + String descriptor() { + return "([Ljava/lang/Object;I)Ljava/lang/Object;"; + } + + String constDescriptor() { + return "([Ljava/lang/Object;I)V"; + } + } + + static class LongConstParameter extends ConstParameter { + long param; + + LongConstParameter(long l) { + param = l; + } + + int compile(Bytecode code) throws CannotCompileException { + code.addLconst(param); + return 2; + } + + String descriptor() { + return "([Ljava/lang/Object;J)Ljava/lang/Object;"; + } + + String constDescriptor() { + return "([Ljava/lang/Object;J)V"; + } + } + + static class StringConstParameter extends ConstParameter { + String param; + + StringConstParameter(String s) { + param = s; + } + + int compile(Bytecode code) throws CannotCompileException { + code.addLdc(param); + return 1; + } + + String descriptor() { + return "([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;"; + } + + String constDescriptor() { + return "([Ljava/lang/Object;Ljava/lang/String;)V"; + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewClass.java b/src/main/java/com/wenshuo/agent/javassist/CtNewClass.java new file mode 100644 index 0000000..1db1989 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtNewClass.java @@ -0,0 +1,126 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.DataOutputStream; +import java.io.IOException; +import com.wenshuo.agent.javassist.bytecode.ClassFile; + +class CtNewClass extends CtClassType { + /* true if the class is an interface. + */ + protected boolean hasConstructor; + + CtNewClass(String name, ClassPool cp, + boolean isInterface, CtClass superclass) { + super(name, cp); + wasChanged = true; + String superName; + if (isInterface || superclass == null) + superName = null; + else + superName = superclass.getName(); + + classfile = new ClassFile(isInterface, name, superName); + if (isInterface && superclass != null) + classfile.setInterfaces(new String[] { superclass.getName() }); + + setModifiers(Modifier.setPublic(getModifiers())); + hasConstructor = isInterface; + } + + protected void extendToString(StringBuffer buffer) { + if (hasConstructor) + buffer.append("hasConstructor "); + + super.extendToString(buffer); + } + + public void addConstructor(CtConstructor c) + throws CannotCompileException + { + hasConstructor = true; + super.addConstructor(c); + } + + public void toBytecode(DataOutputStream out) + throws CannotCompileException, IOException + { + if (!hasConstructor) + try { + inheritAllConstructors(); + hasConstructor = true; + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + + super.toBytecode(out); + } + + /** + * Adds constructors inhrited from the super class. + * + *

After this method is called, the class inherits all the + * constructors from the super class. The added constructor + * calls the super's constructor with the same signature. + */ + public void inheritAllConstructors() + throws CannotCompileException, NotFoundException + { + CtClass superclazz; + CtConstructor[] cs; + + superclazz = getSuperclass(); + cs = superclazz.getDeclaredConstructors(); + + int n = 0; + for (int i = 0; i < cs.length; ++i) { + CtConstructor c = cs[i]; + int mod = c.getModifiers(); + if (isInheritable(mod, superclazz)) { + CtConstructor cons + = CtNewConstructor.make(c.getParameterTypes(), + c.getExceptionTypes(), this); + cons.setModifiers(mod & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)); + addConstructor(cons); + ++n; + } + } + + if (n < 1) + throw new CannotCompileException( + "no inheritable constructor in " + superclazz.getName()); + + } + + private boolean isInheritable(int mod, CtClass superclazz) { + if (Modifier.isPrivate(mod)) + return false; + + if (Modifier.isPackage(mod)) { + String pname = getPackageName(); + String pname2 = superclazz.getPackageName(); + if (pname == null) + return pname2 == null; + else + return pname.equals(pname2); + } + + return true; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewConstructor.java b/src/main/java/com/wenshuo/agent/javassist/CtNewConstructor.java new file mode 100644 index 0000000..18c929d --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtNewConstructor.java @@ -0,0 +1,319 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.Javac; +import com.wenshuo.agent.javassist.compiler.CompileError; +import com.wenshuo.agent.javassist.CtMethod.ConstParameter; + +/** + * A collection of static methods for creating a CtConstructor. + * An instance of this class does not make any sense. + * + *

A class initializer (static constructor) cannot be created by the + * methods in this class. Call makeClassInitializer() in + * CtClass and append code snippet to the body of the class + * initializer obtained by makeClassInitializer(). + * + * @see CtClass#addConstructor(CtConstructor) + * @see CtClass#makeClassInitializer() + */ +public class CtNewConstructor { + /** + * Specifies that no parameters are passed to a super-class' + * constructor. That is, the default constructor is invoked. + */ + public static final int PASS_NONE = 0; // call super() + + /** + * Specifies that parameters are converted into an array of + * Object and passed to a super-class' + * constructor. + */ + public static final int PASS_ARRAY = 1; // an array of parameters + + /** + * Specifies that parameters are passed as is + * to a super-class' constructor. The signature of that + * constructor must be the same as that of the created constructor. + */ + public static final int PASS_PARAMS = 2; + + /** + * Compiles the given source code and creates a constructor. + * The source code must include not only the constructor body + * but the whole declaration. + * + * @param src the source text. + * @param declaring the class to which the created constructor is added. + */ + public static CtConstructor make(String src, CtClass declaring) + throws CannotCompileException + { + Javac compiler = new Javac(declaring); + try { + CtMember obj = compiler.compile(src); + if (obj instanceof CtConstructor) { + // a stack map table has been already created. + return (CtConstructor)obj; + } + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + + throw new CannotCompileException("not a constructor"); + } + + /** + * Creates a public constructor. + * + * @param parameters a list of the parameter types. + * @param exceptions a list of the exception types. + * @param body the source text of the constructor body. + * It must be a block surrounded by {}. + * If it is null, the substituted + * constructor body does nothing except calling + * super(). + * @param declaring the class to which the created method is added. + */ + public static CtConstructor make(CtClass[] parameters, + CtClass[] exceptions, + String body, CtClass declaring) + throws CannotCompileException + { + try { + CtConstructor cc = new CtConstructor(parameters, declaring); + cc.setExceptionTypes(exceptions); + cc.setBody(body); + return cc; + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + } + + /** + * Creates a copy of a constructor. + * This is a convenience method for calling + * {@link CtConstructor#CtConstructor(CtConstructor, CtClass, ClassMap) this constructor}. + * See the description of the constructor for particular behavior of the copying. + * + * @param c the copied constructor. + * @param declaring the class to which the created method is added. + * @param map the hash table associating original class names + * with substituted names. + * It can be null. + * + * @see CtConstructor#CtConstructor(CtConstructor,CtClass,ClassMap) + */ + public static CtConstructor copy(CtConstructor c, CtClass declaring, + ClassMap map) throws CannotCompileException { + return new CtConstructor(c, declaring, map); + } + + /** + * Creates a default (public) constructor. + * + *

The created constructor takes no parameter. It calls + * super(). + */ + public static CtConstructor defaultConstructor(CtClass declaring) + throws CannotCompileException + { + CtConstructor cons = new CtConstructor((CtClass[])null, declaring); + + ConstPool cp = declaring.getClassFile2().getConstPool(); + Bytecode code = new Bytecode(cp, 1, 1); + code.addAload(0); + try { + code.addInvokespecial(declaring.getSuperclass(), + "", "()V"); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + + code.add(Bytecode.RETURN); + + // no need to construct a stack map table. + cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute()); + return cons; + } + + /** + * Creates a public constructor that only calls a constructor + * in the super class. The created constructor receives parameters + * specified by parameters but calls the super's + * constructor without those parameters (that is, it calls the default + * constructor). + * + *

The parameters passed to the created constructor should be + * used for field initialization. CtField.Initializer + * objects implicitly insert initialization code in constructor + * bodies. + * + * @param parameters parameter types + * @param exceptions exception types + * @param declaring the class to which the created constructor + * is added. + * @see CtField.Initializer#byParameter(int) + */ + public static CtConstructor skeleton(CtClass[] parameters, + CtClass[] exceptions, CtClass declaring) + throws CannotCompileException + { + return make(parameters, exceptions, PASS_NONE, + null, null, declaring); + } + + /** + * Creates a public constructor that only calls a constructor + * in the super class. The created constructor receives parameters + * specified by parameters and calls the super's + * constructor with those parameters. + * + * @param parameters parameter types + * @param exceptions exception types + * @param declaring the class to which the created constructor + * is added. + */ + public static CtConstructor make(CtClass[] parameters, + CtClass[] exceptions, CtClass declaring) + throws CannotCompileException + { + return make(parameters, exceptions, PASS_PARAMS, + null, null, declaring); + } + + /** + * Creates a public constructor. + * + *

If howto is PASS_PARAMS, + * the created constructor calls the super's constructor with the + * same signature. The superclass must contain + * a constructor taking the same set of parameters as the created one. + * + *

If howto is PASS_NONE, + * the created constructor calls the super's default constructor. + * The superclass must contain a constructor taking no parameters. + * + *

If howto is PASS_ARRAY, + * the created constructor calls the super's constructor + * with the given parameters in the form of an array of + * Object. The signature of the super's constructor + * must be: + * + *

constructor(Object[] params, <type> cvalue)
+     * 
+ * + *

Here, cvalue is the constant value specified + * by cparam. + * + *

If cparam is null, the signature + * must be: + * + *

constructor(Object[] params)
+ * + *

If body is not null, a copy of that method is + * embedded in the body of the created constructor. + * The embedded method is executed after + * the super's constructor is called and the values of fields are + * initialized. Note that body must not + * be a constructor but a method. + * + *

Since the embedded method is wrapped + * in parameter-conversion code + * as in CtNewMethod.wrapped(), + * the constructor parameters are + * passed in the form of an array of Object. + * The method specified by body must have the + * signature shown below: + * + *

Object method(Object[] params, <type> cvalue)
+ * + *

If cparam is null, the signature + * must be: + * + *

Object method(Object[] params)
+ * + *

Although the type of the returned value is Object, + * the value must be always null. + * + *

Example: + * + *

+     * ClassPool pool = ... ;
+     * CtClass xclass = pool.makeClass("X");
+     * CtMethod method = pool.getMethod("Sample", "m");
+     * xclass.setSuperclass(pool.get("Y"));
+     * CtClass[] argTypes = { CtClass.intType };
+     * ConstParameter cparam = ConstParameter.string("test");
+     * CtConstructor c = CtNewConstructor.make(argTypes, null,
+     *                                  PASS_PARAMS, method, cparam, xclass);
+     * xclass.addConstructor(c);
+ * + *

where the class Sample is as follows: + * + *

+     * public class Sample {
+     *     public Object m(Object[] args, String msg) {
+     *         System.out.println(msg);
+     *         return null;
+     *     }
+     * }
+ * + *

This program produces the following class: + * + *

+     * public class X extends Y {
+     *     public X(int p0) {
+     *         super(p0);
+     *         String msg = "test";
+     *         Object[] args = new Object[] { p0 };
+     *         // begin of copied body
+     *         System.out.println(msg);
+     *         Object result = null;
+     *         // end
+     *     }
+     * }
+ * + * @param parameters a list of the parameter types + * @param exceptions a list of the exceptions + * @param howto how to pass parameters to the super-class' + * constructor (PASS_NONE, + * PASS_ARRAY, + * or PASS_PARAMS) + * @param body appended body (may be null). + * It must be not a constructor but a method. + * @param cparam constant parameter (may be null.) + * @param declaring the class to which the created constructor + * is added. + * + * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass) + */ + public static CtConstructor make(CtClass[] parameters, + CtClass[] exceptions, int howto, + CtMethod body, ConstParameter cparam, + CtClass declaring) + throws CannotCompileException + { + return CtNewWrappedConstructor.wrapped(parameters, exceptions, + howto, body, cparam, declaring); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewMethod.java b/src/main/java/com/wenshuo/agent/javassist/CtNewMethod.java new file mode 100644 index 0000000..d55a464 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtNewMethod.java @@ -0,0 +1,478 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.Javac; +import com.wenshuo.agent.javassist.compiler.CompileError; +import com.wenshuo.agent.javassist.CtMethod.ConstParameter; + +/** + * A collection of static methods for creating a CtMethod. + * An instance of this class does not make any sense. + * + * @see CtClass#addMethod(CtMethod) + */ +public class CtNewMethod { + + /** + * Compiles the given source code and creates a method. + * The source code must include not only the method body + * but the whole declaration, for example, + * + *
"public Object id(Object obj) { return obj; }"
+ * + * @param src the source text. + * @param declaring the class to which the created method is added. + */ + public static CtMethod make(String src, CtClass declaring) + throws CannotCompileException + { + return make(src, declaring, null, null); + } + + /** + * Compiles the given source code and creates a method. + * The source code must include not only the method body + * but the whole declaration, for example, + * + *
"public Object id(Object obj) { return obj; }"
+ * + *

If the source code includes $proceed(), then + * it is compiled into a method call on the specified object. + * + * @param src the source text. + * @param declaring the class to which the created method is added. + * @param delegateObj the source text specifying the object + * that is called on by $proceed(). + * @param delegateMethod the name of the method + * that is called by $proceed(). + */ + public static CtMethod make(String src, CtClass declaring, + String delegateObj, String delegateMethod) + throws CannotCompileException + { + Javac compiler = new Javac(declaring); + try { + if (delegateMethod != null) + compiler.recordProceed(delegateObj, delegateMethod); + + CtMember obj = compiler.compile(src); + if (obj instanceof CtMethod) + return (CtMethod)obj; + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + + throw new CannotCompileException("not a method"); + } + + /** + * Creates a public (non-static) method. The created method cannot + * be changed to a static method later. + * + * @param returnType the type of the returned value. + * @param mname the method name. + * @param parameters a list of the parameter types. + * @param exceptions a list of the exception types. + * @param body the source text of the method body. + * It must be a block surrounded by {}. + * If it is null, the created method + * does nothing except returning zero or null. + * @param declaring the class to which the created method is added. + * @see #make(int, CtClass, String, CtClass[], CtClass[], String, CtClass) + */ + public static CtMethod make(CtClass returnType, + String mname, CtClass[] parameters, + CtClass[] exceptions, + String body, CtClass declaring) + throws CannotCompileException + { + return make(Modifier.PUBLIC, returnType, mname, parameters, exceptions, + body, declaring); + } + + /** + * Creates a method. modifiers can contain + * Modifier.STATIC. + * + * @param modifiers access modifiers. + * @param returnType the type of the returned value. + * @param mname the method name. + * @param parameters a list of the parameter types. + * @param exceptions a list of the exception types. + * @param body the source text of the method body. + * It must be a block surrounded by {}. + * If it is null, the created method + * does nothing except returning zero or null. + * @param declaring the class to which the created method is added. + * + * @see Modifier + */ + public static CtMethod make(int modifiers, CtClass returnType, + String mname, CtClass[] parameters, + CtClass[] exceptions, + String body, CtClass declaring) + throws CannotCompileException + { + try { + CtMethod cm + = new CtMethod(returnType, mname, parameters, declaring); + cm.setModifiers(modifiers); + cm.setExceptionTypes(exceptions); + cm.setBody(body); + return cm; + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + } + + /** + * Creates a copy of a method. This method is provided for creating + * a new method based on an existing method. + * This is a convenience method for calling + * {@link CtMethod#CtMethod(CtMethod, CtClass, ClassMap) this constructor}. + * See the description of the constructor for particular behavior of the copying. + * + * @param src the source method. + * @param declaring the class to which the created method is added. + * @param map the hash table associating original class names + * with substituted names. + * It can be null. + * + * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap) + */ + public static CtMethod copy(CtMethod src, CtClass declaring, + ClassMap map) throws CannotCompileException { + return new CtMethod(src, declaring, map); + } + + /** + * Creates a copy of a method with a new name. + * This method is provided for creating + * a new method based on an existing method. + * This is a convenience method for calling + * {@link CtMethod#CtMethod(CtMethod, CtClass, ClassMap) this constructor}. + * See the description of the constructor for particular behavior of the copying. + * + * @param src the source method. + * @param name the name of the created method. + * @param declaring the class to which the created method is added. + * @param map the hash table associating original class names + * with substituted names. + * It can be null. + * + * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap) + */ + public static CtMethod copy(CtMethod src, String name, CtClass declaring, + ClassMap map) throws CannotCompileException { + CtMethod cm = new CtMethod(src, declaring, map); + cm.setName(name); + return cm; + } + + /** + * Creates a public abstract method. + * + * @param returnType the type of the returned value + * @param mname the method name + * @param parameters a list of the parameter types + * @param exceptions a list of the exception types + * @param declaring the class to which the created method is added. + * + * @see CtMethod#CtMethod(CtClass,String,CtClass[],CtClass) + */ + public static CtMethod abstractMethod(CtClass returnType, + String mname, + CtClass[] parameters, + CtClass[] exceptions, + CtClass declaring) + throws NotFoundException + { + CtMethod cm = new CtMethod(returnType, mname, parameters, declaring); + cm.setExceptionTypes(exceptions); + return cm; + } + + /** + * Creates a public getter method. The getter method returns the value + * of the specified field in the class to which this method is added. + * The created method is initially not static even if the field is + * static. Change the modifiers if the method should be static. + * + * @param methodName the name of the getter + * @param field the field accessed. + */ + public static CtMethod getter(String methodName, CtField field) + throws CannotCompileException + { + FieldInfo finfo = field.getFieldInfo2(); + String fieldType = finfo.getDescriptor(); + String desc = "()" + fieldType; + ConstPool cp = finfo.getConstPool(); + MethodInfo minfo = new MethodInfo(cp, methodName, desc); + minfo.setAccessFlags(AccessFlag.PUBLIC); + + Bytecode code = new Bytecode(cp, 2, 1); + try { + String fieldName = finfo.getName(); + if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) { + code.addAload(0); + code.addGetfield(Bytecode.THIS, fieldName, fieldType); + } + else + code.addGetstatic(Bytecode.THIS, fieldName, fieldType); + + code.addReturn(field.getType()); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + + minfo.setCodeAttribute(code.toCodeAttribute()); + CtClass cc = field.getDeclaringClass(); + // a stack map is not needed. + return new CtMethod(minfo, cc); + } + + /** + * Creates a public setter method. The setter method assigns the + * value of the first parameter to the specified field + * in the class to which this method is added. + * The created method is not static even if the field is + * static. You may not change it to be static + * by setModifiers() in CtBehavior. + * + * @param methodName the name of the setter + * @param field the field accessed. + */ + public static CtMethod setter(String methodName, CtField field) + throws CannotCompileException + { + FieldInfo finfo = field.getFieldInfo2(); + String fieldType = finfo.getDescriptor(); + String desc = "(" + fieldType + ")V"; + ConstPool cp = finfo.getConstPool(); + MethodInfo minfo = new MethodInfo(cp, methodName, desc); + minfo.setAccessFlags(AccessFlag.PUBLIC); + + Bytecode code = new Bytecode(cp, 3, 3); + try { + String fieldName = finfo.getName(); + if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) { + code.addAload(0); + code.addLoad(1, field.getType()); + code.addPutfield(Bytecode.THIS, fieldName, fieldType); + } + else { + code.addLoad(1, field.getType()); + code.addPutstatic(Bytecode.THIS, fieldName, fieldType); + } + + code.addReturn(null); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + + minfo.setCodeAttribute(code.toCodeAttribute()); + CtClass cc = field.getDeclaringClass(); + // a stack map is not needed. + return new CtMethod(minfo, cc); + } + + /** + * Creates a method forwarding to a delegate in + * a super class. The created method calls a method specified + * by delegate with all the parameters passed to the + * created method. If the delegate method returns a value, + * the created method returns that value to the caller. + * The delegate method must be declared in a super class. + * + *

The following method is an example of the created method. + * + *

+     * int f(int p, int q) {
+     *     return super.f(p, q);
+     * }
+ * + *

The name of the created method can be changed by + * setName(). + * + * @param delegate the method that the created method forwards to. + * @param declaring the class to which the created method is + * added. + */ + public static CtMethod delegator(CtMethod delegate, CtClass declaring) + throws CannotCompileException + { + try { + return delegator0(delegate, declaring); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + } + + private static CtMethod delegator0(CtMethod delegate, CtClass declaring) + throws CannotCompileException, NotFoundException + { + MethodInfo deleInfo = delegate.getMethodInfo2(); + String methodName = deleInfo.getName(); + String desc = deleInfo.getDescriptor(); + ConstPool cp = declaring.getClassFile2().getConstPool(); + MethodInfo minfo = new MethodInfo(cp, methodName, desc); + minfo.setAccessFlags(deleInfo.getAccessFlags()); + + ExceptionsAttribute eattr = deleInfo.getExceptionsAttribute(); + if (eattr != null) + minfo.setExceptionsAttribute( + (ExceptionsAttribute)eattr.copy(cp, null)); + + Bytecode code = new Bytecode(cp, 0, 0); + boolean isStatic = Modifier.isStatic(delegate.getModifiers()); + CtClass deleClass = delegate.getDeclaringClass(); + CtClass[] params = delegate.getParameterTypes(); + int s; + if (isStatic) { + s = code.addLoadParameters(params, 0); + code.addInvokestatic(deleClass, methodName, desc); + } + else { + code.addLoad(0, deleClass); + s = code.addLoadParameters(params, 1); + code.addInvokespecial(deleClass, methodName, desc); + } + + code.addReturn(delegate.getReturnType()); + code.setMaxLocals(++s); + code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value + minfo.setCodeAttribute(code.toCodeAttribute()); + // a stack map is not needed. + return new CtMethod(minfo, declaring); + } + + /** + * Creates a wrapped method. The wrapped method receives parameters + * in the form of an array of Object. + * + *

The body of the created method is a copy of the body of the method + * specified by body. However, it is wrapped in + * parameter-conversion code. + * + *

The method specified by body must have this singature: + * + *

Object method(Object[] params, <type> cvalue)
+ * + *

The type of the cvalue depends on + * constParam. + * If constParam is null, the signature + * must be: + * + *

Object method(Object[] params)
+ * + *

The method body copied from body is wrapped in + * parameter-conversion code, which converts parameters specified by + * parameterTypes into an array of Object. + * The returned value is also converted from the Object + * type to the type specified by returnType. Thus, + * the resulting method body is as follows: + * + *

+     * Object[] params = new Object[] { p0, p1, ... };
+     * <type> cvalue = <constant-value>;
+     *  ... copied method body ...
+     * Object result = <returned value>
+     * return (<returnType>)result;
+     * 
+ * + *

The variables p0, p2, ... represent + * formal parameters of the created method. + * The value of cvalue is specified by + * constParam. + * + *

If the type of a parameter or a returned value is a primitive + * type, then the value is converted into a wrapper object such as + * java.lang.Integer. If the type of the returned value + * is void, the returned value is discarded. + * + *

Example: + * + *

+     * ClassPool pool = ... ;
+     * CtClass vec = pool.makeClass("intVector");
+     * vec.setSuperclass(pool.get("java.util.Vector"));
+     * CtMethod addMethod = pool.getMethod("Sample", "add0");
+     *
+     * CtClass[] argTypes = { CtClass.intType };
+     * CtMethod m = CtNewMethod.wrapped(CtClass.voidType, "add", argTypes,
+     *                                  null, addMethod, null, vec);
+     * vec.addMethod(m);
+ * + *

where the class Sample is as follows: + * + *

public class Sample extends java.util.Vector {
+     *     public Object add0(Object[] args) {
+     *         super.addElement(args[0]);
+     *         return null;
+     *     }
+     * }
+ * + *

This program produces a class intVector: + * + *

public class intVector extends java.util.Vector {
+     *     public void add(int p0) {
+     *         Object[] args = new Object[] { p0 };
+     *         // begin of the copied body
+     *         super.addElement(args[0]);
+     *         Object result = null;
+     *         // end
+     *     }
+     * }
+ * + *

Note that the type of the parameter to add() depends + * only on the value of argTypes passed to + * CtNewMethod.wrapped(). Thus, it is easy to + * modify this program to produce a + * StringVector class, which is a vector containing + * only String objects, and other vector classes. + * + * @param returnType the type of the returned value. + * @param mname the method name. + * @param parameterTypes a list of the parameter types. + * @param exceptionTypes a list of the exception types. + * @param body the method body + * (must not be a static method). + * @param constParam the constant parameter + * (maybe null). + * @param declaring the class to which the created method is + * added. + */ + public static CtMethod wrapped(CtClass returnType, + String mname, + CtClass[] parameterTypes, + CtClass[] exceptionTypes, + CtMethod body, ConstParameter constParam, + CtClass declaring) + throws CannotCompileException + { + return CtNewWrappedMethod.wrapped(returnType, mname, parameterTypes, + exceptionTypes, body, constParam, declaring); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewNestedClass.java b/src/main/java/com/wenshuo/agent/javassist/CtNewNestedClass.java new file mode 100644 index 0000000..4fb89b0 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtNewNestedClass.java @@ -0,0 +1,67 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.ClassFile; +import com.wenshuo.agent.javassist.bytecode.AccessFlag; +import com.wenshuo.agent.javassist.bytecode.InnerClassesAttribute; + +/** + * A newly created public nested class. + */ +class CtNewNestedClass extends CtNewClass { + CtNewNestedClass(String realName, ClassPool cp, boolean isInterface, + CtClass superclass) { + super(realName, cp, isInterface, superclass); + } + + /** + * This method does not change the STATIC bit. The original value is kept. + */ + public void setModifiers(int mod) { + mod = mod & ~Modifier.STATIC; + super.setModifiers(mod); + updateInnerEntry(mod, getName(), this, true); + } + + private static void updateInnerEntry(int mod, String name, CtClass clazz, boolean outer) { + ClassFile cf = clazz.getClassFile2(); + InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( + InnerClassesAttribute.tag); + if (ica == null) + return; + + int n = ica.tableLength(); + for (int i = 0; i < n; i++) + if (name.equals(ica.innerClass(i))) { + int acc = ica.accessFlags(i) & AccessFlag.STATIC; + ica.setAccessFlags(i, mod | acc); + String outName = ica.outerClass(i); + if (outName != null && outer) + try { + CtClass parent = clazz.getClassPool().get(outName); + updateInnerEntry(mod, name, parent, false); + } + catch (NotFoundException e) { + throw new RuntimeException("cannot find the declaring class: " + + outName); + } + + break; + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java b/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java new file mode 100644 index 0000000..d507162 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java @@ -0,0 +1,103 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.CtMethod.ConstParameter; + +class CtNewWrappedConstructor extends CtNewWrappedMethod { + private static final int PASS_NONE = CtNewConstructor.PASS_NONE; + // private static final int PASS_ARRAY = CtNewConstructor.PASS_ARRAY; + private static final int PASS_PARAMS = CtNewConstructor.PASS_PARAMS; + + public static CtConstructor wrapped(CtClass[] parameterTypes, + CtClass[] exceptionTypes, + int howToCallSuper, + CtMethod body, + ConstParameter constParam, + CtClass declaring) + throws CannotCompileException + { + try { + CtConstructor cons = new CtConstructor(parameterTypes, declaring); + cons.setExceptionTypes(exceptionTypes); + Bytecode code = makeBody(declaring, declaring.getClassFile2(), + howToCallSuper, body, + parameterTypes, constParam); + cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute()); + // a stack map table is not needed. + return cons; + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + } + + protected static Bytecode makeBody(CtClass declaring, ClassFile classfile, + int howToCallSuper, + CtMethod wrappedBody, + CtClass[] parameters, + ConstParameter cparam) + throws CannotCompileException + { + int stacksize, stacksize2; + + int superclazz = classfile.getSuperclassId(); + Bytecode code = new Bytecode(classfile.getConstPool(), 0, 0); + code.setMaxLocals(false, parameters, 0); + code.addAload(0); + if (howToCallSuper == PASS_NONE) { + stacksize = 1; + code.addInvokespecial(superclazz, "", "()V"); + } + else if (howToCallSuper == PASS_PARAMS) { + stacksize = code.addLoadParameters(parameters, 1) + 1; + code.addInvokespecial(superclazz, "", + Descriptor.ofConstructor(parameters)); + } + else { + stacksize = compileParameterList(code, parameters, 1); + String desc; + if (cparam == null) { + stacksize2 = 2; + desc = ConstParameter.defaultConstDescriptor(); + } + else { + stacksize2 = cparam.compile(code) + 2; + desc = cparam.constDescriptor(); + } + + if (stacksize < stacksize2) + stacksize = stacksize2; + + code.addInvokespecial(superclazz, "", desc); + } + + if (wrappedBody == null) + code.add(Bytecode.RETURN); + else { + stacksize2 = makeBody0(declaring, classfile, wrappedBody, + false, parameters, CtClass.voidType, + cparam, code); + if (stacksize < stacksize2) + stacksize = stacksize2; + } + + code.setMaxStack(stacksize); + return code; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedMethod.java b/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedMethod.java new file mode 100644 index 0000000..da2ad8b --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedMethod.java @@ -0,0 +1,199 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.JvstCodeGen; +import java.util.Hashtable; +import com.wenshuo.agent.javassist.CtMethod.ConstParameter; + +class CtNewWrappedMethod { + + private static final String addedWrappedMethod = "_added_m$"; + + public static CtMethod wrapped(CtClass returnType, String mname, + CtClass[] parameterTypes, + CtClass[] exceptionTypes, + CtMethod body, ConstParameter constParam, + CtClass declaring) + throws CannotCompileException + { + CtMethod mt = new CtMethod(returnType, mname, parameterTypes, + declaring); + mt.setModifiers(body.getModifiers()); + try { + mt.setExceptionTypes(exceptionTypes); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + + Bytecode code = makeBody(declaring, declaring.getClassFile2(), body, + parameterTypes, returnType, constParam); + MethodInfo minfo = mt.getMethodInfo2(); + minfo.setCodeAttribute(code.toCodeAttribute()); + // a stack map has been already created. + return mt; + } + + static Bytecode makeBody(CtClass clazz, ClassFile classfile, + CtMethod wrappedBody, + CtClass[] parameters, + CtClass returnType, + ConstParameter cparam) + throws CannotCompileException + { + boolean isStatic = Modifier.isStatic(wrappedBody.getModifiers()); + Bytecode code = new Bytecode(classfile.getConstPool(), 0, 0); + int stacksize = makeBody0(clazz, classfile, wrappedBody, isStatic, + parameters, returnType, cparam, code); + code.setMaxStack(stacksize); + code.setMaxLocals(isStatic, parameters, 0); + return code; + } + + /* The generated method body does not need a stack map table + * because it does not contain a branch instruction. + */ + protected static int makeBody0(CtClass clazz, ClassFile classfile, + CtMethod wrappedBody, + boolean isStatic, CtClass[] parameters, + CtClass returnType, ConstParameter cparam, + Bytecode code) + throws CannotCompileException + { + if (!(clazz instanceof CtClassType)) + throw new CannotCompileException("bad declaring class" + + clazz.getName()); + + if (!isStatic) + code.addAload(0); + + int stacksize = compileParameterList(code, parameters, + (isStatic ? 0 : 1)); + int stacksize2; + String desc; + if (cparam == null) { + stacksize2 = 0; + desc = ConstParameter.defaultDescriptor(); + } + else { + stacksize2 = cparam.compile(code); + desc = cparam.descriptor(); + } + + checkSignature(wrappedBody, desc); + + String bodyname; + try { + bodyname = addBodyMethod((CtClassType)clazz, classfile, + wrappedBody); + /* if an exception is thrown below, the method added above + * should be removed. (future work :<) + */ + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + + if (isStatic) + code.addInvokestatic(Bytecode.THIS, bodyname, desc); + else + code.addInvokespecial(Bytecode.THIS, bodyname, desc); + + compileReturn(code, returnType); // consumes 2 stack entries + + if (stacksize < stacksize2 + 2) + stacksize = stacksize2 + 2; + + return stacksize; + } + + private static void checkSignature(CtMethod wrappedBody, + String descriptor) + throws CannotCompileException + { + if (!descriptor.equals(wrappedBody.getMethodInfo2().getDescriptor())) + throw new CannotCompileException( + "wrapped method with a bad signature: " + + wrappedBody.getDeclaringClass().getName() + + '.' + wrappedBody.getName()); + } + + private static String addBodyMethod(CtClassType clazz, + ClassFile classfile, + CtMethod src) + throws BadBytecode, CannotCompileException + { + Hashtable bodies = clazz.getHiddenMethods(); + String bodyname = (String)bodies.get(src); + if (bodyname == null) { + do { + bodyname = addedWrappedMethod + clazz.getUniqueNumber(); + } while (classfile.getMethod(bodyname) != null); + ClassMap map = new ClassMap(); + map.put(src.getDeclaringClass().getName(), clazz.getName()); + MethodInfo body = new MethodInfo(classfile.getConstPool(), + bodyname, src.getMethodInfo2(), + map); + int acc = body.getAccessFlags(); + body.setAccessFlags(AccessFlag.setPrivate(acc)); + body.addAttribute(new SyntheticAttribute(classfile.getConstPool())); + // a stack map is copied. rebuilding it is not needed. + classfile.addMethod(body); + bodies.put(src, bodyname); + CtMember.Cache cache = clazz.hasMemberCache(); + if (cache != null) + cache.addMethod(new CtMethod(body, clazz)); + } + + return bodyname; + } + + /* compileParameterList() returns the stack size used + * by the produced code. + * + * @param regno the index of the local variable in which + * the first argument is received. + * (0: static method, 1: regular method.) + */ + static int compileParameterList(Bytecode code, + CtClass[] params, int regno) { + return JvstCodeGen.compileParameterList(code, params, regno); + } + + /* + * The produced codes cosume 1 or 2 stack entries. + */ + private static void compileReturn(Bytecode code, CtClass type) { + if (type.isPrimitive()) { + CtPrimitiveType pt = (CtPrimitiveType)type; + if (pt != CtClass.voidType) { + String wrapper = pt.getWrapperName(); + code.addCheckcast(wrapper); + code.addInvokevirtual(wrapper, pt.getGetMethodName(), + pt.getGetMethodDescriptor()); + } + + code.addOpcode(pt.getReturnOp()); + } + else { + code.addCheckcast(type); + code.addOpcode(Bytecode.ARETURN); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/CtPrimitiveType.java b/src/main/java/com/wenshuo/agent/javassist/CtPrimitiveType.java new file mode 100644 index 0000000..bdd961b --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/CtPrimitiveType.java @@ -0,0 +1,112 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +/** + * An instance of CtPrimitiveType represents a primitive type. + * It is obtained from CtClass. + */ +public final class CtPrimitiveType extends CtClass { + private char descriptor; + private String wrapperName; + private String getMethodName; + private String mDescriptor; + private int returnOp; + private int arrayType; + private int dataSize; + + CtPrimitiveType(String name, char desc, String wrapper, + String methodName, String mDesc, int opcode, int atype, + int size) { + super(name); + descriptor = desc; + wrapperName = wrapper; + getMethodName = methodName; + mDescriptor = mDesc; + returnOp = opcode; + arrayType = atype; + dataSize = size; + } + + /** + * Returns true if this object represents a primitive + * Java type: boolean, byte, char, short, int, long, float, double, + * or void. + */ + public boolean isPrimitive() { return true; } + + /** + * Returns the modifiers for this type. + * For decoding, use javassist.Modifier. + * + * @see Modifier + */ + public int getModifiers() { + return Modifier.PUBLIC | Modifier.FINAL; + } + + /** + * Returns the descriptor representing this type. + * For example, if the type is int, then the descriptor is I. + */ + public char getDescriptor() { return descriptor; } + + /** + * Returns the name of the wrapper class. + * For example, if the type is int, then the wrapper class is + * java.lang.Integer. + */ + public String getWrapperName() { return wrapperName; } + + /** + * Returns the name of the method for retrieving the value + * from the wrapper object. + * For example, if the type is int, then the method name is + * intValue. + */ + public String getGetMethodName() { return getMethodName; } + + /** + * Returns the descriptor of the method for retrieving the value + * from the wrapper object. + * For example, if the type is int, then the method descriptor is + * ()I. + */ + public String getGetMethodDescriptor() { return mDescriptor; } + + /** + * Returns the opcode for returning a value of the type. + * For example, if the type is int, then the returned opcode is + * javassit.bytecode.Opcode.IRETURN. + */ + public int getReturnOp() { return returnOp; } + + /** + * Returns the array-type code representing the type. + * It is used for the newarray instruction. + * For example, if the type is int, then this method returns + * javassit.bytecode.Opcode.T_INT. + */ + public int getArrayType() { return arrayType; } + + /** + * Returns the data size of the primitive type. + * If the type is long or double, this method returns 2. + * Otherwise, it returns 1. + */ + public int getDataSize() { return dataSize; } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/Loader.java b/src/main/java/com/wenshuo/agent/javassist/Loader.java new file mode 100644 index 0000000..2c2e52f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/Loader.java @@ -0,0 +1,430 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.*; +import java.util.Hashtable; +import java.util.Vector; +import java.security.ProtectionDomain; + +/** + * The class loader for Javassist. + * + *

This is a sample class loader using ClassPool. + * Unlike a regular class loader, this class loader obtains bytecode + * from a ClassPool. + * + *

Note that Javassist can be used without this class loader; programmers + * can define their own versions of class loader. They can run + * a program even without any user-defined class loader if that program + * is statically translated with Javassist. + * This class loader is just provided as a utility class. + * + *

Suppose that an instance of MyTranslator implementing + * the interface Translator is responsible for modifying + * class files. + * The startup program of an application using MyTranslator + * should be something like this: + * + *

+ * import com.wenshuo.agent.javassist.*;
+ *
+ * public class Main {
+ *   public static void main(String[] args) throws Throwable {
+ *     MyTranslator myTrans = new MyTranslator();
+ *     ClassPool cp = ClassPool.getDefault();
+ *     Loader cl = new Loader(cp);
+ *     cl.addTranslator(cp, myTrans);
+ *     cl.run("MyApp", args);
+ *   }
+ * }
+ * 
+ * + *

Class MyApp is the main program of the application. + * + *

This program should be executed as follows: + * + *

+ * % java Main arg1 arg2...
+ * 
+ * + *

It modifies the class MyApp with a MyTranslator + * object before the JVM loads it. + * Then it calls main() in MyApp with arguments + * arg1, arg2, ... + * + *

This program execution is equivalent to: + * + *

+ * % java MyApp arg1 arg2...
+ * 
+ * + *

except that classes are translated by MyTranslator + * at load time. + * + *

If only a particular class must be modified when it is loaded, + * the startup program can be simpler; MyTranslator is + * unnecessary. For example, if only a class test.Rectangle + * is modified, the main() method above will be the following: + * + *

+ * ClassPool cp = ClassPool.getDefault();
+ * Loader cl = new Loader(cp);
+ * CtClass ct = cp.get("test.Rectangle");
+ * ct.setSuperclass(cp.get("test.Point"));
+ * cl.run("MyApp", args);
+ * + *

This program changes the super class of the test.Rectangle + * class. + * + *

Note 1: + * + *

This class loader does not allow the users to intercept the loading + * of java.* and javax.* classes (and + * sun.*, org.xml.*, ...) unless + * Loader.doDelegation is false. This is because + * the JVM prohibits a user class loader from loading a system class. + * Also see Note 2. + * If this behavior is not appropriate, a subclass of Loader + * must be defined and loadClassByDelegation() must be overridden. + * + *

Note 2: + * + *

If classes are loaded with different class loaders, they belong to + * separate name spaces. If class C is loaded by a class + * loader CL, all classes that the class C + * refers to are also loaded by CL. However, if CL + * delegates the loading of the class C to CL', + * then those classes that the class C refers to + * are loaded by a parent class loader CL' + * instead of CL. + * + *

If an object of class C is assigned + * to a variable of class C belonging to a different name + * space, then a ClassCastException is thrown. + * + *

Because of the fact above, this loader delegates only the loading of + * javassist.Loader + * and classes included in package java.* and + * javax.* to the parent class + * loader. Other classes are directly loaded by this loader. + * + *

For example, suppose that java.lang.String would be loaded + * by this loader while java.io.File is loaded by the parent + * class loader. If the constructor of java.io.File is called + * with an instance of java.lang.String, then it may throw + * an exception since it accepts an instance of only the + * java.lang.String loaded by the parent class loader. + * + * @see javassist.ClassPool + * @see javassist.Translator + */ +public class Loader extends ClassLoader { + private Hashtable notDefinedHere; // must be atomic. + private Vector notDefinedPackages; // must be atomic. + private ClassPool source; + private Translator translator; + private ProtectionDomain domain; + + /** + * Specifies the algorithm of class loading. + * + *

This class loader uses the parent class loader for + * java.* and javax.* classes. + * If this variable doDelegation + * is false, this class loader does not delegate those + * classes to the parent class loader. + * + *

The default value is true. + */ + public boolean doDelegation = true; + + /** + * Creates a new class loader. + */ + public Loader() { + this(null); + } + + /** + * Creates a new class loader. + * + * @param cp the source of class files. + */ + public Loader(ClassPool cp) { + init(cp); + } + + /** + * Creates a new class loader + * using the specified parent class loader for delegation. + * + * @param parent the parent class loader. + * @param cp the source of class files. + */ + public Loader(ClassLoader parent, ClassPool cp) { + super(parent); + init(cp); + } + + private void init(ClassPool cp) { + notDefinedHere = new Hashtable(); + notDefinedPackages = new Vector(); + source = cp; + translator = null; + domain = null; + delegateLoadingOf("javassist.Loader"); + } + + /** + * Records a class so that the loading of that class is delegated + * to the parent class loader. + * + *

If the given class name ends with . (dot), then + * that name is interpreted as a package name. All the classes + * in that package and the sub packages are delegated. + */ + public void delegateLoadingOf(String classname) { + if (classname.endsWith(".")) + notDefinedPackages.addElement(classname); + else + notDefinedHere.put(classname, this); + } + + /** + * Sets the protection domain for the classes handled by this class + * loader. Without registering an appropriate protection domain, + * the program loaded by this loader will not work with a security + * manager or a signed jar file. + */ + public void setDomain(ProtectionDomain d) { + domain = d; + } + + /** + * Sets the soruce ClassPool. + */ + public void setClassPool(ClassPool cp) { + source = cp; + } + + /** + * Adds a translator, which is called whenever a class is loaded. + * + * @param cp the ClassPool object for obtaining + * a class file. + * @param t a translator. + * @throws NotFoundException if t.start() throws an exception. + * @throws CannotCompileException if t.start() throws an exception. + */ + public void addTranslator(ClassPool cp, Translator t) + throws NotFoundException, CannotCompileException { + source = cp; + translator = t; + t.start(cp); + } + + /** + * Loads a class with an instance of Loader + * and calls main() of that class. + * + *

This method calls run(). + * + * @param args command line parameters. + *
  {@code args[0]} is the class name to be loaded. + *
  {@code args[1..n]} are parameters passed + * to the target {@code main()}. + * + * @see javassist.Loader#run(String[]) + */ + public static void main(String[] args) throws Throwable { + Loader cl = new Loader(); + cl.run(args); + } + + /** + * Loads a class and calls main() in that class. + * + * @param args command line parameters. + * + *
  {@code args[0]} is the class name to be loaded. + *
  {@code args[1..n]} are parameters passed + * to the target {@code main()}. + */ + public void run(String[] args) throws Throwable { + int n = args.length - 1; + if (n >= 0) { + String[] args2 = new String[n]; + for (int i = 0; i < n; ++i) + args2[i] = args[i + 1]; + + run(args[0], args2); + } + } + + /** + * Loads a class and calls main() in that class. + * + * @param classname the loaded class. + * @param args parameters passed to main(). + */ + public void run(String classname, String[] args) throws Throwable { + Class c = loadClass(classname); + try { + c.getDeclaredMethod("main", new Class[] { String[].class }).invoke( + null, + new Object[] { args }); + } + catch (java.lang.reflect.InvocationTargetException e) { + throw e.getTargetException(); + } + } + + /** + * Requests the class loader to load a class. + */ + protected Class loadClass(String name, boolean resolve) + throws ClassFormatError, ClassNotFoundException { + name = name.intern(); + synchronized (name) { + Class c = findLoadedClass(name); + if (c == null) + c = loadClassByDelegation(name); + + if (c == null) + c = findClass(name); + + if (c == null) + c = delegateToParent(name); + + if (resolve) + resolveClass(c); + + return c; + } + } + + /** + * Finds the specified class using ClassPath. + * If the source throws an exception, this returns null. + * + *

This method can be overridden by a subclass of + * Loader. Note that the overridden method must not throw + * an exception when it just fails to find a class file. + * + * @return null if the specified class could not be found. + * @throws ClassNotFoundException if an exception is thrown while + * obtaining a class file. + */ + protected Class findClass(String name) throws ClassNotFoundException { + byte[] classfile; + try { + if (source != null) { + if (translator != null) + translator.onLoad(source, name); + + try { + classfile = source.get(name).toBytecode(); + } + catch (NotFoundException e) { + return null; + } + } + else { + String jarname = "/" + name.replace('.', '/') + ".class"; + InputStream in = this.getClass().getResourceAsStream(jarname); + if (in == null) + return null; + + classfile = ClassPoolTail.readStream(in); + } + } + catch (Exception e) { + throw new ClassNotFoundException( + "caught an exception while obtaining a class file for " + + name, e); + } + + int i = name.lastIndexOf('.'); + if (i != -1) { + String pname = name.substring(0, i); + if (getPackage(pname) == null) + try { + definePackage( + pname, null, null, null, null, null, null, null); + } + catch (IllegalArgumentException e) { + // ignore. maybe the package object for the same + // name has been created just right away. + } + } + + if (domain == null) + return defineClass(name, classfile, 0, classfile.length); + else + return defineClass(name, classfile, 0, classfile.length, domain); + } + + protected Class loadClassByDelegation(String name) + throws ClassNotFoundException + { + /* The swing components must be loaded by a system + * class loader. + * javax.swing.UIManager loads a (concrete) subclass + * of LookAndFeel by a system class loader and cast + * an instance of the class to LookAndFeel for + * (maybe) a security reason. To avoid failure of + * type conversion, LookAndFeel must not be loaded + * by this class loader. + */ + + Class c = null; + if (doDelegation) + if (name.startsWith("java.") + || name.startsWith("javax.") + || name.startsWith("sun.") + || name.startsWith("com.sun.") + || name.startsWith("org.w3c.") + || name.startsWith("org.xml.") + || notDelegated(name)) + c = delegateToParent(name); + + return c; + } + + private boolean notDelegated(String name) { + if (notDefinedHere.get(name) != null) + return true; + + int n = notDefinedPackages.size(); + for (int i = 0; i < n; ++i) + if (name.startsWith((String)notDefinedPackages.elementAt(i))) + return true; + + return false; + } + + protected Class delegateToParent(String classname) + throws ClassNotFoundException + { + ClassLoader cl = getParent(); + if (cl != null) + return cl.loadClass(classname); + else + return findSystemClass(classname); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/LoaderClassPath.java b/src/main/java/com/wenshuo/agent/javassist/LoaderClassPath.java new file mode 100644 index 0000000..e7c1029 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/LoaderClassPath.java @@ -0,0 +1,96 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.InputStream; +import java.net.URL; +import java.lang.ref.WeakReference; + +/** + * A class search-path representing a class loader. + * + *

It is used for obtaining a class file from the given + * class loader by getResourceAsStream(). + * The LoaderClassPath refers to the class loader through + * WeakReference. If the class loader is garbage collected, + * the other search pathes are examined. + * + *

The given class loader must have both getResourceAsStream() + * and getResource(). + * + * @author Bill Burke + * @author Shigeru Chiba + * + * @see ClassPool#insertClassPath(ClassPath) + * @see ClassPool#appendClassPath(ClassPath) + * @see ClassClassPath + */ +public class LoaderClassPath implements ClassPath { + private WeakReference clref; + + /** + * Creates a search path representing a class loader. + */ + public LoaderClassPath(ClassLoader cl) { + clref = new WeakReference(cl); + } + + public String toString() { + Object cl = null; + if (clref != null) + cl = clref.get(); + + return cl == null ? "" : cl.toString(); + } + + /** + * Obtains a class file from the class loader. + * This method calls getResourceAsStream(String) + * on the class loader. + */ + public InputStream openClassfile(String classname) { + String cname = classname.replace('.', '/') + ".class"; + ClassLoader cl = (ClassLoader)clref.get(); + if (cl == null) + return null; // not found + else + return cl.getResourceAsStream(cname); + } + + /** + * Obtains the URL of the specified class file. + * This method calls getResource(String) + * on the class loader. + * + * @return null if the class file could not be found. + */ + public URL find(String classname) { + String cname = classname.replace('.', '/') + ".class"; + ClassLoader cl = (ClassLoader)clref.get(); + if (cl == null) + return null; // not found + else + return cl.getResource(cname); + } + + /** + * Closes this class path. + */ + public void close() { + clref = null; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/Modifier.java b/src/main/java/com/wenshuo/agent/javassist/Modifier.java new file mode 100644 index 0000000..92f709c --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/Modifier.java @@ -0,0 +1,219 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import com.wenshuo.agent.javassist.bytecode.AccessFlag; + +/** + * The Modifier class provides static methods and constants to decode + * class and member access modifiers. The constant values are equivalent + * to the corresponding values in javassist.bytecode.AccessFlag. + * + *

All the methods/constants in this class are compatible with + * ones in java.lang.reflect.Modifier. + * + * @see CtClass#getModifiers() + */ +public class Modifier { + public static final int PUBLIC = AccessFlag.PUBLIC; + public static final int PRIVATE = AccessFlag.PRIVATE; + public static final int PROTECTED = AccessFlag.PROTECTED; + public static final int STATIC = AccessFlag.STATIC; + public static final int FINAL = AccessFlag.FINAL; + public static final int SYNCHRONIZED = AccessFlag.SYNCHRONIZED; + public static final int VOLATILE = AccessFlag.VOLATILE; + public static final int VARARGS = AccessFlag.VARARGS; + public static final int TRANSIENT = AccessFlag.TRANSIENT; + public static final int NATIVE = AccessFlag.NATIVE; + public static final int INTERFACE = AccessFlag.INTERFACE; + public static final int ABSTRACT = AccessFlag.ABSTRACT; + public static final int STRICT = AccessFlag.STRICT; + public static final int ANNOTATION = AccessFlag.ANNOTATION; + public static final int ENUM = AccessFlag.ENUM; + + /** + * Returns true if the modifiers include the public + * modifier. + */ + public static boolean isPublic(int mod) { + return (mod & PUBLIC) != 0; + } + + /** + * Returns true if the modifiers include the private + * modifier. + */ + public static boolean isPrivate(int mod) { + return (mod & PRIVATE) != 0; + } + + /** + * Returns true if the modifiers include the protected + * modifier. + */ + public static boolean isProtected(int mod) { + return (mod & PROTECTED) != 0; + } + + /** + * Returns true if the modifiers do not include either + * public, protected, or private. + */ + public static boolean isPackage(int mod) { + return (mod & (PUBLIC | PRIVATE | PROTECTED)) == 0; + } + + /** + * Returns true if the modifiers include the static + * modifier. + */ + public static boolean isStatic(int mod) { + return (mod & STATIC) != 0; + } + + /** + * Returns true if the modifiers include the final + * modifier. + */ + public static boolean isFinal(int mod) { + return (mod & FINAL) != 0; + } + + /** + * Returns true if the modifiers include the synchronized + * modifier. + */ + public static boolean isSynchronized(int mod) { + return (mod & SYNCHRONIZED) != 0; + } + + /** + * Returns true if the modifiers include the volatile + * modifier. + */ + public static boolean isVolatile(int mod) { + return (mod & VOLATILE) != 0; + } + + /** + * Returns true if the modifiers include the transient + * modifier. + */ + public static boolean isTransient(int mod) { + return (mod & TRANSIENT) != 0; + } + + /** + * Returns true if the modifiers include the native + * modifier. + */ + public static boolean isNative(int mod) { + return (mod & NATIVE) != 0; + } + + /** + * Returns true if the modifiers include the interface + * modifier. + */ + public static boolean isInterface(int mod) { + return (mod & INTERFACE) != 0; + } + + /** + * Returns true if the modifiers include the annotation + * modifier. + * + * @since 3.2 + */ + public static boolean isAnnotation(int mod) { + return (mod & ANNOTATION) != 0; + } + + /** + * Returns true if the modifiers include the enum + * modifier. + * + * @since 3.2 + */ + public static boolean isEnum(int mod) { + return (mod & ENUM) != 0; + } + + /** + * Returns true if the modifiers include the abstract + * modifier. + */ + public static boolean isAbstract(int mod) { + return (mod & ABSTRACT) != 0; + } + + /** + * Returns true if the modifiers include the strictfp + * modifier. + */ + public static boolean isStrict(int mod) { + return (mod & STRICT) != 0; + } + + /** + * Truns the public bit on. The protected and private bits are + * cleared. + */ + public static int setPublic(int mod) { + return (mod & ~(PRIVATE | PROTECTED)) | PUBLIC; + } + + /** + * Truns the protected bit on. The protected and public bits are + * cleared. + */ + public static int setProtected(int mod) { + return (mod & ~(PRIVATE | PUBLIC)) | PROTECTED; + } + + /** + * Truns the private bit on. The protected and private bits are + * cleared. + */ + public static int setPrivate(int mod) { + return (mod & ~(PROTECTED | PUBLIC)) | PRIVATE; + } + + /** + * Clears the public, protected, and private bits. + */ + public static int setPackage(int mod) { + return (mod & ~(PROTECTED | PUBLIC | PRIVATE)); + } + + /** + * Clears a specified bit in mod. + */ + public static int clear(int mod, int clearBit) { + return mod & ~clearBit; + } + + /** + * Return a string describing the access modifier flags in + * the specified modifier. + * + * @param mod modifier flags. + */ + public static String toString(int mod) { + return java.lang.reflect.Modifier.toString(mod); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/NotFoundException.java b/src/main/java/com/wenshuo/agent/javassist/NotFoundException.java new file mode 100644 index 0000000..5569375 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/NotFoundException.java @@ -0,0 +1,30 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +/** + * Signals that something could not be found. + */ +public class NotFoundException extends Exception { + public NotFoundException(String msg) { + super(msg); + } + + public NotFoundException(String msg, Exception e) { + super(msg + " because of " + e.toString()); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/SerialVersionUID.java b/src/main/java/com/wenshuo/agent/javassist/SerialVersionUID.java new file mode 100644 index 0000000..37f9add --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/SerialVersionUID.java @@ -0,0 +1,213 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.*; +import java.lang.reflect.Modifier; + +import com.wenshuo.agent.javassist.bytecode.*; +import java.util.*; +import java.security.*; + +/** + * Utility for calculating serialVersionUIDs for Serializable classes. + * + * @author Bob Lee (crazybob@crazybob.org) + * @author modified by Shigeru Chiba + */ +public class SerialVersionUID { + + /** + * Adds serialVersionUID if one does not already exist. Call this before + * modifying a class to maintain serialization compatability. + */ + public static void setSerialVersionUID(CtClass clazz) + throws CannotCompileException, NotFoundException + { + // check for pre-existing field. + try { + clazz.getDeclaredField("serialVersionUID"); + return; + } + catch (NotFoundException e) {} + + // check if the class is serializable. + if (!isSerializable(clazz)) + return; + + // add field with default value. + CtField field = new CtField(CtClass.longType, "serialVersionUID", + clazz); + field.setModifiers(Modifier.PRIVATE | Modifier.STATIC | + Modifier.FINAL); + clazz.addField(field, calculateDefault(clazz) + "L"); + } + + /** + * Does the class implement Serializable? + */ + private static boolean isSerializable(CtClass clazz) + throws NotFoundException + { + ClassPool pool = clazz.getClassPool(); + return clazz.subtypeOf(pool.get("java.io.Serializable")); + } + + /** + * Calculate default value. See Java Serialization Specification, Stream + * Unique Identifiers. + * + * @since 3.20 + */ + public static long calculateDefault(CtClass clazz) + throws CannotCompileException + { + try { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bout); + ClassFile classFile = clazz.getClassFile(); + + // class name. + String javaName = javaName(clazz); + out.writeUTF(javaName); + + CtMethod[] methods = clazz.getDeclaredMethods(); + + // class modifiers. + int classMods = clazz.getModifiers(); + if ((classMods & Modifier.INTERFACE) != 0) + if (methods.length > 0) + classMods = classMods | Modifier.ABSTRACT; + else + classMods = classMods & ~Modifier.ABSTRACT; + + out.writeInt(classMods); + + // interfaces. + String[] interfaces = classFile.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + interfaces[i] = javaName(interfaces[i]); + + Arrays.sort(interfaces); + for (int i = 0; i < interfaces.length; i++) + out.writeUTF(interfaces[i]); + + // fields. + CtField[] fields = clazz.getDeclaredFields(); + Arrays.sort(fields, new Comparator() { + public int compare(Object o1, Object o2) { + CtField field1 = (CtField)o1; + CtField field2 = (CtField)o2; + return field1.getName().compareTo(field2.getName()); + } + }); + + for (int i = 0; i < fields.length; i++) { + CtField field = (CtField) fields[i]; + int mods = field.getModifiers(); + if (((mods & Modifier.PRIVATE) == 0) || + ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) { + out.writeUTF(field.getName()); + out.writeInt(mods); + out.writeUTF(field.getFieldInfo2().getDescriptor()); + } + } + + // static initializer. + if (classFile.getStaticInitializer() != null) { + out.writeUTF(""); + out.writeInt(Modifier.STATIC); + out.writeUTF("()V"); + } + + // constructors. + CtConstructor[] constructors = clazz.getDeclaredConstructors(); + Arrays.sort(constructors, new Comparator() { + public int compare(Object o1, Object o2) { + CtConstructor c1 = (CtConstructor)o1; + CtConstructor c2 = (CtConstructor)o2; + return c1.getMethodInfo2().getDescriptor().compareTo( + c2.getMethodInfo2().getDescriptor()); + } + }); + + for (int i = 0; i < constructors.length; i++) { + CtConstructor constructor = constructors[i]; + int mods = constructor.getModifiers(); + if ((mods & Modifier.PRIVATE) == 0) { + out.writeUTF(""); + out.writeInt(mods); + out.writeUTF(constructor.getMethodInfo2() + .getDescriptor().replace('/', '.')); + } + } + + // methods. + Arrays.sort(methods, new Comparator() { + public int compare(Object o1, Object o2) { + CtMethod m1 = (CtMethod)o1; + CtMethod m2 = (CtMethod)o2; + int value = m1.getName().compareTo(m2.getName()); + if (value == 0) + value = m1.getMethodInfo2().getDescriptor() + .compareTo(m2.getMethodInfo2().getDescriptor()); + + return value; + } + }); + + for (int i = 0; i < methods.length; i++) { + CtMethod method = methods[i]; + int mods = method.getModifiers() + & (Modifier.PUBLIC | Modifier.PRIVATE + | Modifier.PROTECTED | Modifier.STATIC + | Modifier.FINAL | Modifier.SYNCHRONIZED + | Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT); + if ((mods & Modifier.PRIVATE) == 0) { + out.writeUTF(method.getName()); + out.writeInt(mods); + out.writeUTF(method.getMethodInfo2() + .getDescriptor().replace('/', '.')); + } + } + + // calculate hash. + out.flush(); + MessageDigest digest = MessageDigest.getInstance("SHA"); + byte[] digested = digest.digest(bout.toByteArray()); + long hash = 0; + for (int i = Math.min(digested.length, 8) - 1; i >= 0; i--) + hash = (hash << 8) | (digested[i] & 0xFF); + + return hash; + } + catch (IOException e) { + throw new CannotCompileException(e); + } + catch (NoSuchAlgorithmException e) { + throw new CannotCompileException(e); + } + } + + private static String javaName(CtClass clazz) { + return Descriptor.toJavaName(Descriptor.toJvmName(clazz)); + } + + private static String javaName(String name) { + return Descriptor.toJavaName(Descriptor.toJvmName(name)); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/Translator.java b/src/main/java/com/wenshuo/agent/javassist/Translator.java new file mode 100644 index 0000000..1492f31 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/Translator.java @@ -0,0 +1,71 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +/** + * An observer of Loader. + * The users can define a class implementing this + * interface and attach an instance of that class to a + * Loader object so that it can translate a class file + * when the class file is loaded into the JVM. + * + * @see Loader#addTranslator(ClassPool, Translator) + */ +public interface Translator { + /** + * Is invoked by a Loader for initialization + * when the object is attached to the Loader object. + * This method can be used for getting (for caching) some + * CtClass objects that will be accessed + * in onLoad() in Translator. + * + * @param pool the ClassPool that this translator + * should use. + * @see Loader + * @throws NotFoundException if a CtClass cannot be found. + * @throws CannotCompileException if the initialization by this method + * fails. + */ + void start(ClassPool pool) + throws NotFoundException, CannotCompileException; + + /** + * Is invoked by a Loader for notifying that + * a class is loaded. The Loader calls + * + *

+     * pool.get(classname).toBytecode()
+ * + * to read the class file after onLoad() returns. + * + *

classname may be the name of a class + * that has not been created yet. + * If so, onLoad() must create that class so that + * the Loader can read it after onLoad() + * returns. + * + * @param pool the ClassPool that this translator + * should use. + * @param classname the name of the class being loaded. + * @see Loader + * @throws NotFoundException if a CtClass cannot be found. + * @throws CannotCompileException if the code transformation + * by this method fails. + */ + void onLoad(ClassPool pool, String classname) + throws NotFoundException, CannotCompileException; +} diff --git a/src/main/java/com/wenshuo/agent/javassist/URLClassPath.java b/src/main/java/com/wenshuo/agent/javassist/URLClassPath.java new file mode 100644 index 0000000..a66b87a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/URLClassPath.java @@ -0,0 +1,179 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist; + +import java.io.*; +import java.net.*; + +/** + * A class search-path specified with URL (http). + * + * @see javassist.ClassPath + * @see ClassPool#insertClassPath(ClassPath) + * @see ClassPool#appendClassPath(ClassPath) + */ +public class URLClassPath implements ClassPath { + protected String hostname; + protected int port; + protected String directory; + protected String packageName; + + /** + * Creates a search path specified with URL (http). + * + *

This search path is used only if a requested + * class name starts with the name specified by packageName. + * If packageName is "org.javassist." and a requested class is + * "org.javassist.test.Main", then the given URL is used for loading that class. + * The URLClassPath obtains a class file from: + * + *

http://www.javassist.org:80/java/classes/org/javassist/test/Main.class
+     * 
+ * + *

Here, we assume that host is "www.javassist.org", + * port is 80, and directory is "/java/classes/". + * + *

If packageName is null, the URL is used + * for loading any class. + * + * @param host host name + * @param port port number + * @param directory directory name ending with "/". + * It can be "/" (root directory). + * It must start with "/". + * @param packageName package name. It must end with "." (dot). + */ + public URLClassPath(String host, int port, + String directory, String packageName) { + hostname = host; + this.port = port; + this.directory = directory; + this.packageName = packageName; + } + + public String toString() { + return hostname + ":" + port + directory; + } + + /** + * Opens a class file with http. + * + * @return null if the class file could not be found. + */ + public InputStream openClassfile(String classname) { + try { + URLConnection con = openClassfile0(classname); + if (con != null) + return con.getInputStream(); + } + catch (IOException e) {} + return null; // not found + } + + private URLConnection openClassfile0(String classname) throws IOException { + if (packageName == null || classname.startsWith(packageName)) { + String jarname + = directory + classname.replace('.', '/') + ".class"; + return fetchClass0(hostname, port, jarname); + } + else + return null; // not found + } + + /** + * Returns the URL. + * + * @return null if the class file could not be obtained. + */ + public URL find(String classname) { + try { + URLConnection con = openClassfile0(classname); + InputStream is = con.getInputStream(); + if (is != null) { + is.close(); + return con.getURL(); + } + } + catch (IOException e) {} + return null; + } + + /** + * Closes this class path. + */ + public void close() {} + + /** + * Reads a class file on an http server. + * + * @param host host name + * @param port port number + * @param directory directory name ending with "/". + * It can be "/" (root directory). + * It must start with "/". + * @param classname fully-qualified class name + */ + public static byte[] fetchClass(String host, int port, + String directory, String classname) + throws IOException + { + byte[] b; + URLConnection con = fetchClass0(host, port, + directory + classname.replace('.', '/') + ".class"); + int size = con.getContentLength(); + InputStream s = con.getInputStream(); + try { + if (size <= 0) + b = ClassPoolTail.readStream(s); + else { + b = new byte[size]; + int len = 0; + do { + int n = s.read(b, len, size - len); + if (n < 0) + throw new IOException("the stream was closed: " + + classname); + + len += n; + } while (len < size); + } + } + finally { + s.close(); + } + + return b; + } + + private static URLConnection fetchClass0(String host, int port, + String filename) + throws IOException + { + URL url; + try { + url = new URL("http", host, port, filename); + } + catch (MalformedURLException e) { + // should never reache here. + throw new IOException("invalid URL?"); + } + + URLConnection con = url.openConnection(); + con.connect(); + return con; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/AccessFlag.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/AccessFlag.java new file mode 100644 index 0000000..e550e23 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/AccessFlag.java @@ -0,0 +1,134 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +/** + * A support class providing static methods and constants + * for access modifiers such as public, private, ... + */ +public class AccessFlag { + public static final int PUBLIC = 0x0001; + public static final int PRIVATE = 0x0002; + public static final int PROTECTED = 0x0004; + public static final int STATIC = 0x0008; + public static final int FINAL = 0x0010; + public static final int SYNCHRONIZED = 0x0020; + public static final int VOLATILE = 0x0040; + public static final int BRIDGE = 0x0040; // for method_info + public static final int TRANSIENT = 0x0080; + public static final int VARARGS = 0x0080; // for method_info + public static final int NATIVE = 0x0100; + public static final int INTERFACE = 0x0200; + public static final int ABSTRACT = 0x0400; + public static final int STRICT = 0x0800; + public static final int SYNTHETIC = 0x1000; + public static final int ANNOTATION = 0x2000; + public static final int ENUM = 0x4000; + public static final int MANDATED = 0x8000; + + public static final int SUPER = 0x0020; + + // Note: 0x0020 is assigned to both ACC_SUPER and ACC_SYNCHRONIZED + // although java.lang.reflect.Modifier does not recognize ACC_SUPER. + + /** + * Truns the public bit on. The protected and private bits are + * cleared. + */ + public static int setPublic(int accflags) { + return (accflags & ~(PRIVATE | PROTECTED)) | PUBLIC; + } + + /** + * Truns the protected bit on. The protected and public bits are + * cleared. + */ + public static int setProtected(int accflags) { + return (accflags & ~(PRIVATE | PUBLIC)) | PROTECTED; + } + + /** + * Truns the private bit on. The protected and private bits are + * cleared. + */ + public static int setPrivate(int accflags) { + return (accflags & ~(PROTECTED | PUBLIC)) | PRIVATE; + } + + /** + * Clears the public, protected, and private bits. + */ + public static int setPackage(int accflags) { + return (accflags & ~(PROTECTED | PUBLIC | PRIVATE)); + } + + /** + * Returns true if the access flags include the public bit. + */ + public static boolean isPublic(int accflags) { + return (accflags & PUBLIC) != 0; + } + + /** + * Returns true if the access flags include the protected bit. + */ + public static boolean isProtected(int accflags) { + return (accflags & PROTECTED) != 0; + } + + /** + * Returns true if the access flags include the private bit. + */ + public static boolean isPrivate(int accflags) { + return (accflags & PRIVATE) != 0; + } + + /** + * Returns true if the access flags include neither public, protected, + * or private. + */ + public static boolean isPackage(int accflags) { + return (accflags & (PROTECTED | PUBLIC | PRIVATE)) == 0; + } + + /** + * Clears a specified bit in accflags. + */ + public static int clear(int accflags, int clearBit) { + return accflags & ~clearBit; + } + + /** + * Converts a javassist.Modifier into + * a javassist.bytecode.AccessFlag. + * + * @param modifier javassist.Modifier + */ + public static int of(int modifier) { + return modifier; + } + + /** + * Converts a javassist.bytecode.AccessFlag + * into a javassist.Modifier. + * + * @param accflags javassist.bytecode.Accessflag + */ + public static int toModifier(int accflags) { + return accflags; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java new file mode 100644 index 0000000..4bbddf3 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java @@ -0,0 +1,160 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.bytecode.annotation.AnnotationsWriter; +import com.wenshuo.agent.javassist.bytecode.annotation.MemberValue; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +/** + * A class representing AnnotationDefault_attribute. + * + *

For example, if you declare the following annotation type: + * + *

+ * @interface Author {
+ *   String name() default "Shakespeare";
+ *   int age() default 99;
+ * }
+ * 
+ * + *

The defautl values of name and age + * are stored as annotation default attributes in Author.class. + * The following code snippet obtains the default value of name: + * + *

+ * ClassPool pool = ...
+ * CtClass cc = pool.get("Author");
+ * CtMethod cm = cc.getDeclaredMethod("age");
+ * MethodInfo minfo = cm.getMethodInfo();
+ * AnnotationDefaultAttribute ada
+ *         = (AnnotationDefaultAttribute)
+ *           minfo.getAttribute(AnnotationDefaultAttribute.tag);
+ * MemberValue value = ada.getDefaultValue());    // default value of age
+ * 
+ * + *

If the following statement is executed after the code above, + * the default value of age is set to 80: + * + *

+ * ada.setDefaultValue(new IntegerMemberValue(minfo.getConstPool(), 80));
+ * 
+ * + * @see AnnotationsAttribute + * @see javassist.bytecode.annotation.MemberValue + */ + +public class AnnotationDefaultAttribute extends AttributeInfo { + /** + * The name of the AnnotationDefault attribute. + */ + public static final String tag = "AnnotationDefault"; + + /** + * Constructs an AnnotationDefault_attribute. + * + * @param cp constant pool + * @param info the contents of this attribute. It does not + * include attribute_name_index or + * attribute_length. + */ + public AnnotationDefaultAttribute(ConstPool cp, byte[] info) { + super(cp, tag, info); + } + + /** + * Constructs an empty AnnotationDefault_attribute. + * The default value can be set by setDefaultValue(). + * + * @param cp constant pool + * @see #setDefaultValue(javassist.bytecode.annotation.MemberValue) + */ + public AnnotationDefaultAttribute(ConstPool cp) { + this(cp, new byte[] { 0, 0 }); + } + + /** + * @param n the attribute name. + */ + AnnotationDefaultAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Copies this attribute and returns a new copy. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + AnnotationsAttribute.Copier copier + = new AnnotationsAttribute.Copier(info, constPool, newCp, classnames); + try { + copier.memberValue(0); + return new AnnotationDefaultAttribute(newCp, copier.close()); + } + catch (Exception e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Obtains the default value represented by this attribute. + */ + public MemberValue getDefaultValue() + { + try { + return new AnnotationsAttribute.Parser(info, constPool) + .parseMemberValue(); + } + catch (Exception e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Changes the default value represented by this attribute. + * + * @param value the new value. + * @see javassist.bytecode.annotation.Annotation#createMemberValue(ConstPool, CtClass) + */ + public void setDefaultValue(MemberValue value) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + AnnotationsWriter writer = new AnnotationsWriter(output, constPool); + try { + value.write(writer); + writer.close(); + } + catch (IOException e) { + throw new RuntimeException(e); // should never reach here. + } + + set(output.toByteArray()); + + } + + /** + * Returns a string representation of this object. + */ + public String toString() { + return getDefaultValue().toString(); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java new file mode 100644 index 0000000..59fa6a4 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java @@ -0,0 +1,732 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.util.Map; +import java.util.HashMap; +import java.io.IOException; +import java.io.DataInputStream; +import java.io.ByteArrayOutputStream; +import com.wenshuo.agent.javassist.bytecode.annotation.*; + +/** + * A class representing + * RuntimeVisibleAnnotations_attribute and + * RuntimeInvisibleAnnotations_attribute. + * + *

To obtain an AnnotationAttribute object, invoke + * getAttribute(AnnotationsAttribute.visibleTag) + * in ClassFile, MethodInfo, + * or FieldInfo. The obtained attribute is a + * runtime visible annotations attribute. + * If the parameter is + * AnnotationAttribute.invisibleTag, then the obtained + * attribute is a runtime invisible one. + * + *

For example, + * + *

+ * import com.wenshuo.agent.javassist.bytecode.annotation.Annotation;
+ *    :
+ * CtMethod m = ... ;
+ * MethodInfo minfo = m.getMethodInfo();
+ * AnnotationsAttribute attr = (AnnotationsAttribute)
+ *         minfo.getAttribute(AnnotationsAttribute.invisibleTag);
+ * Annotation an = attr.getAnnotation("Author");
+ * String s = ((StringMemberValue)an.getMemberValue("name")).getValue();
+ * System.out.println("@Author(name=" + s + ")");
+ * 
+ * + *

This code snippet retrieves an annotation of the type Author + * from the MethodInfo object specified by minfo. + * Then, it prints the value of name in Author. + * + *

If the annotation type Author is annotated by a meta annotation: + * + *

+ * @Retention(RetentionPolicy.RUNTIME)
+ * 
+ * + *

Then Author is visible at runtime. Therefore, the third + * statement of the code snippet above must be changed into: + * + *

+ * AnnotationsAttribute attr = (AnnotationsAttribute)
+ *         minfo.getAttribute(AnnotationsAttribute.visibleTag);
+ * 
+ * + *

The attribute tag must be visibleTag instead of + * invisibleTag. + * + *

If the member value of an annotation is not specified, the default value + * is used as that member value. If so, getMemberValue() in + * Annotation returns null + * since the default value is not included in the + * AnnotationsAttribute. It is included in the + * AnnotationDefaultAttribute of the method declared in the + * annotation type. + * + *

If you want to record a new AnnotationAttribute object, execute the + * following snippet: + * + *

+ * ClassFile cf = ... ;
+ * ConstPool cp = cf.getConstPool();
+ * AnnotationsAttribute attr
+ *     = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
+ * Annotation a = new Annotation("Author", cp);
+ * a.addMemberValue("name", new StringMemberValue("Chiba", cp));
+ * attr.setAnnotation(a);
+ * cf.addAttribute(attr);
+ * cf.setVersionToJava5();
+ * 
+ * + *

The last statement is necessary if the class file was produced by + * javac of JDK 1.4 or earlier. Otherwise, it is not necessary. + * + * @see AnnotationDefaultAttribute + * @see javassist.bytecode.annotation.Annotation + */ +public class AnnotationsAttribute extends AttributeInfo { + /** + * The name of the RuntimeVisibleAnnotations attribute. + */ + public static final String visibleTag = "RuntimeVisibleAnnotations"; + + /** + * The name of the RuntimeInvisibleAnnotations attribute. + */ + public static final String invisibleTag = "RuntimeInvisibleAnnotations"; + + /** + * Constructs a Runtime(In)VisibleAnnotations_attribute. + * + * @param cp constant pool + * @param attrname attribute name (visibleTag or + * invisibleTag). + * @param info the contents of this attribute. It does not + * include attribute_name_index or + * attribute_length. + */ + public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) { + super(cp, attrname, info); + } + + /** + * Constructs an empty + * Runtime(In)VisibleAnnotations_attribute. + * A new annotation can be later added to the created attribute + * by setAnnotations(). + * + * @param cp constant pool + * @param attrname attribute name (visibleTag or + * invisibleTag). + * @see #setAnnotations(Annotation[]) + */ + public AnnotationsAttribute(ConstPool cp, String attrname) { + this(cp, attrname, new byte[] { 0, 0 }); + } + + /** + * @param n the attribute name. + */ + AnnotationsAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Returns num_annotations. + */ + public int numAnnotations() { + return ByteArray.readU16bit(info, 0); + } + + /** + * Copies this attribute and returns a new copy. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + Copier copier = new Copier(info, constPool, newCp, classnames); + try { + copier.annotationArray(); + return new AnnotationsAttribute(newCp, getName(), copier.close()); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Parses the annotations and returns a data structure representing + * the annotation with the specified type. See also + * getAnnotations() as to the returned data structure. + * + * @param type the annotation type. + * @return null if the specified annotation type is not included. + * @see #getAnnotations() + */ + public Annotation getAnnotation(String type) { + Annotation[] annotations = getAnnotations(); + for (int i = 0; i < annotations.length; i++) { + if (annotations[i].getTypeName().equals(type)) + return annotations[i]; + } + + return null; + } + + /** + * Adds an annotation. If there is an annotation with the same type, + * it is removed before the new annotation is added. + * + * @param annotation the added annotation. + */ + public void addAnnotation(Annotation annotation) { + String type = annotation.getTypeName(); + Annotation[] annotations = getAnnotations(); + for (int i = 0; i < annotations.length; i++) { + if (annotations[i].getTypeName().equals(type)) { + annotations[i] = annotation; + setAnnotations(annotations); + return; + } + } + + Annotation[] newlist = new Annotation[annotations.length + 1]; + System.arraycopy(annotations, 0, newlist, 0, annotations.length); + newlist[annotations.length] = annotation; + setAnnotations(newlist); + } + + /** + * Parses the annotations and returns a data structure representing + * that parsed annotations. Note that changes of the node values of the + * returned tree are not reflected on the annotations represented by + * this object unless the tree is copied back to this object by + * setAnnotations(). + * + * @see #setAnnotations(Annotation[]) + */ + public Annotation[] getAnnotations() { + try { + return new Parser(info, constPool).parseAnnotations(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Changes the annotations represented by this object according to + * the given array of Annotation objects. + * + * @param annotations the data structure representing the + * new annotations. + */ + public void setAnnotations(Annotation[] annotations) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + AnnotationsWriter writer = new AnnotationsWriter(output, constPool); + try { + int n = annotations.length; + writer.numAnnotations(n); + for (int i = 0; i < n; ++i) + annotations[i].write(writer); + + writer.close(); + } + catch (IOException e) { + throw new RuntimeException(e); // should never reach here. + } + + set(output.toByteArray()); + } + + /** + * Changes the annotations. A call to this method is equivalent to: + *

setAnnotations(new Annotation[] { annotation })
+ * + * @param annotation the data structure representing + * the new annotation. + */ + public void setAnnotation(Annotation annotation) { + setAnnotations(new Annotation[] { annotation }); + } + + /** + * @param oldname a JVM class name. + * @param newname a JVM class name. + */ + void renameClass(String oldname, String newname) { + HashMap map = new HashMap(); + map.put(oldname, newname); + renameClass(map); + } + + void renameClass(Map classnames) { + Renamer renamer = new Renamer(info, getConstPool(), classnames); + try { + renamer.annotationArray(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + void getRefClasses(Map classnames) { renameClass(classnames); } + + /** + * Returns a string representation of this object. + */ + public String toString() { + Annotation[] a = getAnnotations(); + StringBuilder sbuf = new StringBuilder(); + int i = 0; + while (i < a.length) { + sbuf.append(a[i++].toString()); + if (i != a.length) + sbuf.append(", "); + } + + return sbuf.toString(); + } + + static class Walker { + byte[] info; + + Walker(byte[] attrInfo) { + info = attrInfo; + } + + final void parameters() throws Exception { + int numParam = info[0] & 0xff; + parameters(numParam, 1); + } + + void parameters(int numParam, int pos) throws Exception { + for (int i = 0; i < numParam; ++i) + pos = annotationArray(pos); + } + + final void annotationArray() throws Exception { + annotationArray(0); + } + + final int annotationArray(int pos) throws Exception { + int num = ByteArray.readU16bit(info, pos); + return annotationArray(pos + 2, num); + } + + int annotationArray(int pos, int num) throws Exception { + for (int i = 0; i < num; ++i) + pos = annotation(pos); + + return pos; + } + + final int annotation(int pos) throws Exception { + int type = ByteArray.readU16bit(info, pos); + int numPairs = ByteArray.readU16bit(info, pos + 2); + return annotation(pos + 4, type, numPairs); + } + + int annotation(int pos, int type, int numPairs) throws Exception { + for (int j = 0; j < numPairs; ++j) + pos = memberValuePair(pos); + + return pos; + } + + /** + * {@code element_value_paris} + */ + final int memberValuePair(int pos) throws Exception { + int nameIndex = ByteArray.readU16bit(info, pos); + return memberValuePair(pos + 2, nameIndex); + } + + /** + * {@code element_value_paris[]} + */ + int memberValuePair(int pos, int nameIndex) throws Exception { + return memberValue(pos); + } + + /** + * {@code element_value} + */ + final int memberValue(int pos) throws Exception { + int tag = info[pos] & 0xff; + if (tag == 'e') { + int typeNameIndex = ByteArray.readU16bit(info, pos + 1); + int constNameIndex = ByteArray.readU16bit(info, pos + 3); + enumMemberValue(pos, typeNameIndex, constNameIndex); + return pos + 5; + } + else if (tag == 'c') { + int index = ByteArray.readU16bit(info, pos + 1); + classMemberValue(pos, index); + return pos + 3; + } + else if (tag == '@') + return annotationMemberValue(pos + 1); + else if (tag == '[') { + int num = ByteArray.readU16bit(info, pos + 1); + return arrayMemberValue(pos + 3, num); + } + else { // primitive types or String. + int index = ByteArray.readU16bit(info, pos + 1); + constValueMember(tag, index); + return pos + 3; + } + } + + /** + * {@code const_value_index} + */ + void constValueMember(int tag, int index) throws Exception {} + + /** + * {@code enum_const_value} + */ + void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) + throws Exception { + } + + /** + * {@code class_info_index} + */ + void classMemberValue(int pos, int index) throws Exception {} + + /** + * {@code annotation_value} + */ + int annotationMemberValue(int pos) throws Exception { + return annotation(pos); + } + + /** + * {@code array_value} + */ + int arrayMemberValue(int pos, int num) throws Exception { + for (int i = 0; i < num; ++i) { + pos = memberValue(pos); + } + + return pos; + } + } + + static class Renamer extends Walker { + ConstPool cpool; + Map classnames; + + /** + * Constructs a renamer. It renames some class names + * into the new names specified by map. + * + * @param info the annotations attribute. + * @param cp the constant pool. + * @param map pairs of replaced and substituted class names. + * It can be null. + */ + Renamer(byte[] info, ConstPool cp, Map map) { + super(info); + cpool = cp; + classnames = map; + } + + int annotation(int pos, int type, int numPairs) throws Exception { + renameType(pos - 4, type); + return super.annotation(pos, type, numPairs); + } + + void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) + throws Exception + { + renameType(pos + 1, typeNameIndex); + super.enumMemberValue(pos, typeNameIndex, constNameIndex); + } + + void classMemberValue(int pos, int index) throws Exception { + renameType(pos + 1, index); + super.classMemberValue(pos, index); + } + + private void renameType(int pos, int index) { + String name = cpool.getUtf8Info(index); + String newName = Descriptor.rename(name, classnames); + if (!name.equals(newName)) { + int index2 = cpool.addUtf8Info(newName); + ByteArray.write16bit(index2, info, pos); + } + } + } + + static class Copier extends Walker { + ByteArrayOutputStream output; + AnnotationsWriter writer; + ConstPool srcPool, destPool; + Map classnames; + + /** + * Constructs a copier. This copier renames some class names + * into the new names specified by map when it copies + * an annotation attribute. + * + * @param info the source attribute. + * @param src the constant pool of the source class. + * @param dest the constant pool of the destination class. + * @param map pairs of replaced and substituted class names. + * It can be null. + */ + Copier(byte[] info, ConstPool src, ConstPool dest, Map map) { + this(info, src, dest, map, true); + } + + Copier(byte[] info, ConstPool src, ConstPool dest, Map map, boolean makeWriter) { + super(info); + output = new ByteArrayOutputStream(); + if (makeWriter) + writer = new AnnotationsWriter(output, dest); + + srcPool = src; + destPool = dest; + classnames = map; + } + + byte[] close() throws IOException { + writer.close(); + return output.toByteArray(); + } + + void parameters(int numParam, int pos) throws Exception { + writer.numParameters(numParam); + super.parameters(numParam, pos); + } + + int annotationArray(int pos, int num) throws Exception { + writer.numAnnotations(num); + return super.annotationArray(pos, num); + } + + int annotation(int pos, int type, int numPairs) throws Exception { + writer.annotation(copyType(type), numPairs); + return super.annotation(pos, type, numPairs); + } + + int memberValuePair(int pos, int nameIndex) throws Exception { + writer.memberValuePair(copy(nameIndex)); + return super.memberValuePair(pos, nameIndex); + } + + void constValueMember(int tag, int index) throws Exception { + writer.constValueIndex(tag, copy(index)); + super.constValueMember(tag, index); + } + + void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) + throws Exception + { + writer.enumConstValue(copyType(typeNameIndex), copy(constNameIndex)); + super.enumMemberValue(pos, typeNameIndex, constNameIndex); + } + + void classMemberValue(int pos, int index) throws Exception { + writer.classInfoIndex(copyType(index)); + super.classMemberValue(pos, index); + } + + int annotationMemberValue(int pos) throws Exception { + writer.annotationValue(); + return super.annotationMemberValue(pos); + } + + int arrayMemberValue(int pos, int num) throws Exception { + writer.arrayValue(num); + return super.arrayMemberValue(pos, num); + } + + /** + * Copies a constant pool entry into the destination constant pool + * and returns the index of the copied entry. + * + * @param srcIndex the index of the copied entry into the source + * constant pool. + * @return the index of the copied item into the destination + * constant pool. + */ + int copy(int srcIndex) { + return srcPool.copy(srcIndex, destPool, classnames); + } + + /** + * Copies a constant pool entry into the destination constant pool + * and returns the index of the copied entry. That entry must be + * a Utf8Info representing a class name in the L; form. + * + * @param srcIndex the index of the copied entry into the source + * constant pool. + * @return the index of the copied item into the destination + * constant pool. + */ + int copyType(int srcIndex) { + String name = srcPool.getUtf8Info(srcIndex); + String newName = Descriptor.rename(name, classnames); + return destPool.addUtf8Info(newName); + } + } + + static class Parser extends Walker { + ConstPool pool; + Annotation[][] allParams; // all parameters + Annotation[] allAnno; // all annotations + Annotation currentAnno; // current annotation + MemberValue currentMember; // current member + + /** + * Constructs a parser. This parser constructs a parse tree of + * the annotations. + * + * @param info the attribute. + * @param src the constant pool. + */ + Parser(byte[] info, ConstPool cp) { + super(info); + pool = cp; + } + + Annotation[][] parseParameters() throws Exception { + parameters(); + return allParams; + } + + Annotation[] parseAnnotations() throws Exception { + annotationArray(); + return allAnno; + } + + MemberValue parseMemberValue() throws Exception { + memberValue(0); + return currentMember; + } + + void parameters(int numParam, int pos) throws Exception { + Annotation[][] params = new Annotation[numParam][]; + for (int i = 0; i < numParam; ++i) { + pos = annotationArray(pos); + params[i] = allAnno; + } + + allParams = params; + } + + int annotationArray(int pos, int num) throws Exception { + Annotation[] array = new Annotation[num]; + for (int i = 0; i < num; ++i) { + pos = annotation(pos); + array[i] = currentAnno; + } + + allAnno = array; + return pos; + } + + int annotation(int pos, int type, int numPairs) throws Exception { + currentAnno = new Annotation(type, pool); + return super.annotation(pos, type, numPairs); + } + + int memberValuePair(int pos, int nameIndex) throws Exception { + pos = super.memberValuePair(pos, nameIndex); + currentAnno.addMemberValue(nameIndex, currentMember); + return pos; + } + + void constValueMember(int tag, int index) throws Exception { + MemberValue m; + ConstPool cp = pool; + switch (tag) { + case 'B' : + m = new ByteMemberValue(index, cp); + break; + case 'C' : + m = new CharMemberValue(index, cp); + break; + case 'D' : + m = new DoubleMemberValue(index, cp); + break; + case 'F' : + m = new FloatMemberValue(index, cp); + break; + case 'I' : + m = new IntegerMemberValue(index, cp); + break; + case 'J' : + m = new LongMemberValue(index, cp); + break; + case 'S' : + m = new ShortMemberValue(index, cp); + break; + case 'Z' : + m = new BooleanMemberValue(index, cp); + break; + case 's' : + m = new StringMemberValue(index, cp); + break; + default : + throw new RuntimeException("unknown tag:" + tag); + } + + currentMember = m; + super.constValueMember(tag, index); + } + + void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) + throws Exception + { + currentMember = new EnumMemberValue(typeNameIndex, + constNameIndex, pool); + super.enumMemberValue(pos, typeNameIndex, constNameIndex); + } + + void classMemberValue(int pos, int index) throws Exception { + currentMember = new ClassMemberValue(index, pool); + super.classMemberValue(pos, index); + } + + int annotationMemberValue(int pos) throws Exception { + Annotation anno = currentAnno; + pos = super.annotationMemberValue(pos); + currentMember = new AnnotationMemberValue(currentAnno, pool); + currentAnno = anno; + return pos; + } + + int arrayMemberValue(int pos, int num) throws Exception { + ArrayMemberValue amv = new ArrayMemberValue(pool); + MemberValue[] elements = new MemberValue[num]; + for (int i = 0; i < num; ++i) { + pos = memberValue(pos); + elements[i] = currentMember; + } + + amv.setValue(elements); + currentMember = amv; + return pos; + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java new file mode 100644 index 0000000..b8f799a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java @@ -0,0 +1,303 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.ArrayList; +import java.util.ListIterator; +import java.util.List; +import java.util.Iterator; + +// Note: if you define a new subclass of AttributeInfo, then +// update AttributeInfo.read(), .copy(), and (maybe) write(). + +/** + * attribute_info structure. + */ +public class AttributeInfo { + protected ConstPool constPool; + int name; + byte[] info; + + protected AttributeInfo(ConstPool cp, int attrname, byte[] attrinfo) { + constPool = cp; + name = attrname; + info = attrinfo; + } + + protected AttributeInfo(ConstPool cp, String attrname) { + this(cp, attrname, (byte[])null); + } + + /** + * Constructs an attribute_info structure. + * + * @param cp constant pool table + * @param attrname attribute name + * @param attrinfo info field + * of attribute_info structure. + */ + public AttributeInfo(ConstPool cp, String attrname, byte[] attrinfo) { + this(cp, cp.addUtf8Info(attrname), attrinfo); + } + + protected AttributeInfo(ConstPool cp, int n, DataInputStream in) + throws IOException + { + constPool = cp; + name = n; + int len = in.readInt(); + info = new byte[len]; + if (len > 0) + in.readFully(info); + } + + static AttributeInfo read(ConstPool cp, DataInputStream in) + throws IOException + { + int name = in.readUnsignedShort(); + String nameStr = cp.getUtf8Info(name); + char first = nameStr.charAt(0); + if (first < 'M') { + if (first < 'E') { + if (nameStr.equals(AnnotationDefaultAttribute.tag)) + return new AnnotationDefaultAttribute(cp, name, in); + else if (nameStr.equals(BootstrapMethodsAttribute.tag)) + return new BootstrapMethodsAttribute(cp, name, in); + else if (nameStr.equals(CodeAttribute.tag)) + return new CodeAttribute(cp, name, in); + else if (nameStr.equals(ConstantAttribute.tag)) + return new ConstantAttribute(cp, name, in); + else if (nameStr.equals(DeprecatedAttribute.tag)) + return new DeprecatedAttribute(cp, name, in); + } + else { + if (nameStr.equals(EnclosingMethodAttribute.tag)) + return new EnclosingMethodAttribute(cp, name, in); + else if (nameStr.equals(ExceptionsAttribute.tag)) + return new ExceptionsAttribute(cp, name, in); + else if (nameStr.equals(InnerClassesAttribute.tag)) + return new InnerClassesAttribute(cp, name, in); + else if (nameStr.equals(LineNumberAttribute.tag)) + return new LineNumberAttribute(cp, name, in); + else if (nameStr.equals(LocalVariableAttribute.tag)) + return new LocalVariableAttribute(cp, name, in); + else if (nameStr.equals(LocalVariableTypeAttribute.tag)) + return new LocalVariableTypeAttribute(cp, name, in); + } + } + else { + if (first < 'S') { + /* Note that the names of Annotations attributes begin with 'R'. + */ + if (nameStr.equals(MethodParametersAttribute.tag)) + return new MethodParametersAttribute(cp, name, in); + else if (nameStr.equals(AnnotationsAttribute.visibleTag) + || nameStr.equals(AnnotationsAttribute.invisibleTag)) { + // RuntimeVisibleAnnotations or RuntimeInvisibleAnnotations + return new AnnotationsAttribute(cp, name, in); + } + else if (nameStr.equals(ParameterAnnotationsAttribute.visibleTag) + || nameStr.equals(ParameterAnnotationsAttribute.invisibleTag)) + return new ParameterAnnotationsAttribute(cp, name, in); + else if (nameStr.equals(TypeAnnotationsAttribute.visibleTag) + || nameStr.equals(TypeAnnotationsAttribute.invisibleTag)) + return new TypeAnnotationsAttribute(cp, name, in); + } + else { + if (nameStr.equals(SignatureAttribute.tag)) + return new SignatureAttribute(cp, name, in); + else if (nameStr.equals(SourceFileAttribute.tag)) + return new SourceFileAttribute(cp, name, in); + else if (nameStr.equals(SyntheticAttribute.tag)) + return new SyntheticAttribute(cp, name, in); + else if (nameStr.equals(StackMap.tag)) + return new StackMap(cp, name, in); + else if (nameStr.equals(StackMapTable.tag)) + return new StackMapTable(cp, name, in); + } + } + + return new AttributeInfo(cp, name, in); + } + + /** + * Returns an attribute name. + */ + public String getName() { + return constPool.getUtf8Info(name); + } + + /** + * Returns a constant pool table. + */ + public ConstPool getConstPool() { return constPool; } + + /** + * Returns the length of this attribute_info + * structure. + * The returned value is attribute_length + 6. + */ + public int length() { + return info.length + 6; + } + + /** + * Returns the info field + * of this attribute_info structure. + * + *

This method is not available if the object is an instance + * of CodeAttribute. + */ + public byte[] get() { return info; } + + /** + * Sets the info field + * of this attribute_info structure. + * + *

This method is not available if the object is an instance + * of CodeAttribute. + */ + public void set(byte[] newinfo) { info = newinfo; } + + /** + * Makes a copy. Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + int s = info.length; + byte[] srcInfo = info; + byte[] newInfo = new byte[s]; + for (int i = 0; i < s; ++i) + newInfo[i] = srcInfo[i]; + + return new AttributeInfo(newCp, getName(), newInfo); + } + + void write(DataOutputStream out) throws IOException { + out.writeShort(name); + out.writeInt(info.length); + if (info.length > 0) + out.write(info); + } + + static int getLength(ArrayList list) { + int size = 0; + int n = list.size(); + for (int i = 0; i < n; ++i) { + AttributeInfo attr = (AttributeInfo)list.get(i); + size += attr.length(); + } + + return size; + } + + static AttributeInfo lookup(ArrayList list, String name) { + if (list == null) + return null; + + ListIterator iterator = list.listIterator(); + while (iterator.hasNext()) { + AttributeInfo ai = (AttributeInfo)iterator.next(); + if (ai.getName().equals(name)) + return ai; + } + + return null; // no such attribute + } + + static synchronized void remove(ArrayList list, String name) { + if (list == null) + return; + + ListIterator iterator = list.listIterator(); + while (iterator.hasNext()) { + AttributeInfo ai = (AttributeInfo)iterator.next(); + if (ai.getName().equals(name)) + iterator.remove(); + } + } + + static void writeAll(ArrayList list, DataOutputStream out) + throws IOException + { + if (list == null) + return; + + int n = list.size(); + for (int i = 0; i < n; ++i) { + AttributeInfo attr = (AttributeInfo)list.get(i); + attr.write(out); + } + } + + static ArrayList copyAll(ArrayList list, ConstPool cp) { + if (list == null) + return null; + + ArrayList newList = new ArrayList(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + AttributeInfo attr = (AttributeInfo)list.get(i); + newList.add(attr.copy(cp, null)); + } + + return newList; + } + + /* The following two methods are used to implement + * ClassFile.renameClass(). + * Only CodeAttribute, LocalVariableAttribute, + * AnnotationsAttribute, and SignatureAttribute + * override these methods. + */ + void renameClass(String oldname, String newname) {} + void renameClass(Map classnames) {} + + static void renameClass(List attributes, String oldname, String newname) { + Iterator iterator = attributes.iterator(); + while (iterator.hasNext()) { + AttributeInfo ai = (AttributeInfo)iterator.next(); + ai.renameClass(oldname, newname); + } + } + + static void renameClass(List attributes, Map classnames) { + Iterator iterator = attributes.iterator(); + while (iterator.hasNext()) { + AttributeInfo ai = (AttributeInfo)iterator.next(); + ai.renameClass(classnames); + } + } + + void getRefClasses(Map classnames) {} + + static void getRefClasses(List attributes, Map classnames) { + Iterator iterator = attributes.iterator(); + while (iterator.hasNext()) { + AttributeInfo ai = (AttributeInfo)iterator.next(); + ai.getRefClasses(classnames); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/BadBytecode.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/BadBytecode.java new file mode 100644 index 0000000..6aeafc7 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/BadBytecode.java @@ -0,0 +1,40 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +/** + * Signals that a bad bytecode sequence has been found. + */ +public class BadBytecode extends Exception { + public BadBytecode(int opcode) { + super("bytecode " + opcode); + } + + public BadBytecode(String msg) { + super(msg); + } + + public BadBytecode(String msg, Throwable cause) { + super(msg, cause); + } + + public BadBytecode(MethodInfo minfo, Throwable cause) { + super(minfo.toString() + " in " + + minfo.getConstPool().getClassName() + + ": " + cause.getMessage(), cause); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java new file mode 100644 index 0000000..194c431 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java @@ -0,0 +1,123 @@ +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +public class BootstrapMethodsAttribute extends AttributeInfo { + /** + * The name of this attribute "BootstrapMethods". + */ + public static final String tag = "BootstrapMethods"; + + /** + * An element of bootstrap_methods. + */ + public static class BootstrapMethod { + /** + * Constructs an element of bootstrap_methods. + * + * @param method bootstrap_method_ref. + * @param args bootstrap_arguments. + */ + public BootstrapMethod(int method, int[] args) { + methodRef = method; + arguments = args; + } + + /** + * bootstrap_method_ref. + * The value at this index must be a CONSTANT_MethodHandle_info. + */ + public int methodRef; + + /** + * bootstrap_arguments. + */ + public int[] arguments; + } + + BootstrapMethodsAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Constructs a BootstrapMethods attribute. + * + * @param cp a constant pool table. + * @param methods the contents. + */ + public BootstrapMethodsAttribute(ConstPool cp, BootstrapMethod[] methods) { + super(cp, tag); + int size = 2; + for (int i = 0; i < methods.length; i++) + size += 4 + methods[i].arguments.length * 2; + + byte[] data = new byte[size]; + ByteArray.write16bit(methods.length, data, 0); // num_bootstrap_methods + int pos = 2; + for (int i = 0; i < methods.length; i++) { + ByteArray.write16bit(methods[i].methodRef, data, pos); + ByteArray.write16bit(methods[i].arguments.length, data, pos + 2); + int[] args = methods[i].arguments; + pos += 4; + for (int k = 0; k < args.length; k++) { + ByteArray.write16bit(args[k], data, pos); + pos += 2; + } + } + + set(data); + } + + /** + * Obtains bootstrap_methods in this attribute. + * + * @return an array of BootstrapMethod. Since it + * is a fresh copy, modifying the returned array does not + * affect the original contents of this attribute. + */ + public BootstrapMethod[] getMethods() { + byte[] data = this.get(); + int num = ByteArray.readU16bit(data, 0); + BootstrapMethod[] methods = new BootstrapMethod[num]; + int pos = 2; + for (int i = 0; i < num; i++) { + int ref = ByteArray.readU16bit(data, pos); + int len = ByteArray.readU16bit(data, pos + 2); + int[] args = new int[len]; + pos += 4; + for (int k = 0; k < len; k++) { + args[k] = ByteArray.readU16bit(data, pos); + pos += 2; + } + + methods[i] = new BootstrapMethod(ref, args); + } + + return methods; + } + + /** + * Makes a copy. Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + BootstrapMethod[] methods = getMethods(); + ConstPool thisCp = getConstPool(); + for (int i = 0; i < methods.length; i++) { + BootstrapMethod m = methods[i]; + m.methodRef = thisCp.copy(m.methodRef, newCp, classnames); + for (int k = 0; k < m.arguments.length; k++) + m.arguments[k] = thisCp.copy(m.arguments[k], newCp, classnames); + } + + return new BootstrapMethodsAttribute(newCp, methods); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteArray.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteArray.java new file mode 100644 index 0000000..ad8fc3f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteArray.java @@ -0,0 +1,77 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +/** + * A collection of static methods for reading and writing a byte array. + */ +public class ByteArray { + /** + * Reads an unsigned 16bit integer at the index. + */ + public static int readU16bit(byte[] code, int index) { + return ((code[index] & 0xff) << 8) | (code[index + 1] & 0xff); + } + + /** + * Reads a signed 16bit integer at the index. + */ + public static int readS16bit(byte[] code, int index) { + return (code[index] << 8) | (code[index + 1] & 0xff); + } + + /** + * Writes a 16bit integer at the index. + */ + public static void write16bit(int value, byte[] code, int index) { + code[index] = (byte)(value >>> 8); + code[index + 1] = (byte)value; + } + + /** + * Reads a 32bit integer at the index. + */ + public static int read32bit(byte[] code, int index) { + return (code[index] << 24) | ((code[index + 1] & 0xff) << 16) + | ((code[index + 2] & 0xff) << 8) | (code[index + 3] & 0xff); + } + + /** + * Writes a 32bit integer at the index. + */ + public static void write32bit(int value, byte[] code, int index) { + code[index] = (byte)(value >>> 24); + code[index + 1] = (byte)(value >>> 16); + code[index + 2] = (byte)(value >>> 8); + code[index + 3] = (byte)value; + } + + /** + * Copies a 32bit integer. + * + * @param src the source byte array. + * @param isrc the index into the source byte array. + * @param dest the destination byte array. + * @param idest the index into the destination byte array. + */ + static void copy32bit(byte[] src, int isrc, byte[] dest, int idest) { + dest[idest] = src[isrc]; + dest[idest + 1] = src[isrc + 1]; + dest[idest + 2] = src[isrc + 2]; + dest[idest + 3] = src[isrc + 3]; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteStream.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteStream.java new file mode 100644 index 0000000..7206ea7 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteStream.java @@ -0,0 +1,194 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.OutputStream; +import java.io.IOException; + +final class ByteStream extends OutputStream { + private byte[] buf; + private int count; + + public ByteStream() { this(32); } + + public ByteStream(int size) { + buf = new byte[size]; + count = 0; + } + + public int getPos() { return count; } + public int size() { return count; } + + public void writeBlank(int len) { + enlarge(len); + count += len; + } + + public void write(byte[] data) { + write(data, 0, data.length); + } + + public void write(byte[] data, int off, int len) { + enlarge(len); + System.arraycopy(data, off, buf, count, len); + count += len; + } + + public void write(int b) { + enlarge(1); + int oldCount = count; + buf[oldCount] = (byte)b; + count = oldCount + 1; + } + + public void writeShort(int s) { + enlarge(2); + int oldCount = count; + buf[oldCount] = (byte)(s >>> 8); + buf[oldCount + 1] = (byte)s; + count = oldCount + 2; + } + + public void writeInt(int i) { + enlarge(4); + int oldCount = count; + buf[oldCount] = (byte)(i >>> 24); + buf[oldCount + 1] = (byte)(i >>> 16); + buf[oldCount + 2] = (byte)(i >>> 8); + buf[oldCount + 3] = (byte)i; + count = oldCount + 4; + } + + public void writeLong(long i) { + enlarge(8); + int oldCount = count; + buf[oldCount] = (byte)(i >>> 56); + buf[oldCount + 1] = (byte)(i >>> 48); + buf[oldCount + 2] = (byte)(i >>> 40); + buf[oldCount + 3] = (byte)(i >>> 32); + buf[oldCount + 4] = (byte)(i >>> 24); + buf[oldCount + 5] = (byte)(i >>> 16); + buf[oldCount + 6] = (byte)(i >>> 8); + buf[oldCount + 7] = (byte)i; + count = oldCount + 8; + } + + public void writeFloat(float v) { + writeInt(Float.floatToIntBits(v)); + } + + public void writeDouble(double v) { + writeLong(Double.doubleToLongBits(v)); + } + + public void writeUTF(String s) { + int sLen = s.length(); + int pos = count; + enlarge(sLen + 2); + + byte[] buffer = buf; + buffer[pos++] = (byte)(sLen >>> 8); + buffer[pos++] = (byte)sLen; + for (int i = 0; i < sLen; ++i) { + char c = s.charAt(i); + if (0x01 <= c && c <= 0x7f) + buffer[pos++] = (byte)c; + else { + writeUTF2(s, sLen, i); + return; + } + } + + count = pos; + } + + private void writeUTF2(String s, int sLen, int offset) { + int size = sLen; + for (int i = offset; i < sLen; i++) { + int c = s.charAt(i); + if (c > 0x7ff) + size += 2; // 3 bytes code + else if (c == 0 || c > 0x7f) + ++size; // 2 bytes code + } + + if (size > 65535) + throw new RuntimeException( + "encoded string too long: " + sLen + size + " bytes"); + + enlarge(size + 2); + int pos = count; + byte[] buffer = buf; + buffer[pos] = (byte)(size >>> 8); + buffer[pos + 1] = (byte)size; + pos += 2 + offset; + for (int j = offset; j < sLen; ++j) { + int c = s.charAt(j); + if (0x01 <= c && c <= 0x7f) + buffer[pos++] = (byte) c; + else if (c > 0x07ff) { + buffer[pos] = (byte)(0xe0 | ((c >> 12) & 0x0f)); + buffer[pos + 1] = (byte)(0x80 | ((c >> 6) & 0x3f)); + buffer[pos + 2] = (byte)(0x80 | (c & 0x3f)); + pos += 3; + } + else { + buffer[pos] = (byte)(0xc0 | ((c >> 6) & 0x1f)); + buffer[pos + 1] = (byte)(0x80 | (c & 0x3f)); + pos += 2; + } + } + + count = pos; + } + + public void write(int pos, int value) { + buf[pos] = (byte)value; + } + + public void writeShort(int pos, int value) { + buf[pos] = (byte)(value >>> 8); + buf[pos + 1] = (byte)value; + } + + public void writeInt(int pos, int value) { + buf[pos] = (byte)(value >>> 24); + buf[pos + 1] = (byte)(value >>> 16); + buf[pos + 2] = (byte)(value >>> 8); + buf[pos + 3] = (byte)value; + } + + public byte[] toByteArray() { + byte[] buf2 = new byte[count]; + System.arraycopy(buf, 0, buf2, 0, count); + return buf2; + } + + public void writeTo(OutputStream out) throws IOException { + out.write(buf, 0, count); + } + + public void enlarge(int delta) { + int newCount = count + delta; + if (newCount > buf.length) { + int newLen = buf.length << 1; + byte[] newBuf = new byte[newLen > newCount ? newLen : newCount]; + System.arraycopy(buf, 0, newBuf, 0, count); + buf = newBuf; + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/Bytecode.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/Bytecode.java new file mode 100644 index 0000000..e04a85f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/Bytecode.java @@ -0,0 +1,1459 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtPrimitiveType; + +class ByteVector implements Cloneable { + private byte[] buffer; + private int size; + + public ByteVector() { + buffer = new byte[64]; + size = 0; + } + + public Object clone() throws CloneNotSupportedException { + ByteVector bv = (ByteVector)super.clone(); + bv.buffer = (byte[])buffer.clone(); + return bv; + } + + public final int getSize() { return size; } + + public final byte[] copy() { + byte[] b = new byte[size]; + System.arraycopy(buffer, 0, b, 0, size); + return b; + } + + public int read(int offset) { + if (offset < 0 || size <= offset) + throw new ArrayIndexOutOfBoundsException(offset); + + return buffer[offset]; + } + + public void write(int offset, int value) { + if (offset < 0 || size <= offset) + throw new ArrayIndexOutOfBoundsException(offset); + + buffer[offset] = (byte)value; + } + + public void add(int code) { + addGap(1); + buffer[size - 1] = (byte)code; + } + + public void add(int b1, int b2) { + addGap(2); + buffer[size - 2] = (byte)b1; + buffer[size - 1] = (byte)b2; + } + + public void add(int b1, int b2, int b3, int b4) { + addGap(4); + buffer[size - 4] = (byte)b1; + buffer[size - 3] = (byte)b2; + buffer[size - 2] = (byte)b3; + buffer[size - 1] = (byte)b4; + } + + public void addGap(int length) { + if (size + length > buffer.length) { + int newSize = size << 1; + if (newSize < size + length) + newSize = size + length; + + byte[] newBuf = new byte[newSize]; + System.arraycopy(buffer, 0, newBuf, 0, size); + buffer = newBuf; + } + + size += length; + } +} + +/** + * A utility class for producing a bytecode sequence. + * + *

A Bytecode object is an unbounded array + * containing bytecode. For example, + * + *

+ * ConstPool cp = ...;    // constant pool table
+ * Bytecode b = new Bytecode(cp, 1, 0);
+ * b.addIconst(3);
+ * b.addReturn(CtClass.intType);
+ * CodeAttribute ca = b.toCodeAttribute();
+ * + *

This program produces a Code attribute including a bytecode + * sequence: + * + *

+ * iconst_3
+ * ireturn
+ * + * @see ConstPool + * @see CodeAttribute + */ +public class Bytecode extends ByteVector implements Cloneable, Opcode { + /** + * Represents the CtClass file using the + * constant pool table given to this Bytecode object. + */ + public static final CtClass THIS = ConstPool.THIS; + + ConstPool constPool; + int maxStack, maxLocals; + ExceptionTable tryblocks; + private int stackDepth; + + /** + * Constructs a Bytecode object with an empty bytecode + * sequence. + * + *

The parameters stacksize and localvars + * specify initial values + * of max_stack and max_locals. + * They can be changed later. + * + * @param cp constant pool table. + * @param stacksize max_stack. + * @param localvars max_locals. + */ + public Bytecode(ConstPool cp, int stacksize, int localvars) { + constPool = cp; + maxStack = stacksize; + maxLocals = localvars; + tryblocks = new ExceptionTable(cp); + stackDepth = 0; + } + + /** + * Constructs a Bytecode object with an empty bytecode + * sequence. The initial values of max_stack and + * max_locals are zero. + * + * @param cp constant pool table. + * @see Bytecode#setMaxStack(int) + * @see Bytecode#setMaxLocals(int) + */ + public Bytecode(ConstPool cp) { + this(cp, 0, 0); + } + + /** + * Creates and returns a copy of this object. + * The constant pool object is shared between this object + * and the cloned object. + */ + public Object clone() { + try { + Bytecode bc = (Bytecode)super.clone(); + bc.tryblocks = (ExceptionTable)tryblocks.clone(); + return bc; + } + catch (CloneNotSupportedException cnse) { + throw new RuntimeException(cnse); + } + } + + /** + * Gets a constant pool table. + */ + public ConstPool getConstPool() { return constPool; } + + /** + * Returns exception_table. + */ + public ExceptionTable getExceptionTable() { return tryblocks; } + + /** + * Converts to a CodeAttribute. + */ + public CodeAttribute toCodeAttribute() { + return new CodeAttribute(constPool, maxStack, maxLocals, + get(), tryblocks); + } + + /** + * Returns the length of the bytecode sequence. + */ + public int length() { + return getSize(); + } + + /** + * Returns the produced bytecode sequence. + */ + public byte[] get() { + return copy(); + } + + /** + * Gets max_stack. + */ + public int getMaxStack() { return maxStack; } + + /** + * Sets max_stack. + * + *

This value may be automatically updated when an instruction + * is appended. A Bytecode object maintains the current + * stack depth whenever an instruction is added + * by addOpcode(). For example, if DUP is appended, + * the current stack depth is increased by one. If the new stack + * depth is more than max_stack, then it is assigned + * to max_stack. However, if branch instructions are + * appended, the current stack depth may not be correctly maintained. + * + * @see #addOpcode(int) + */ + public void setMaxStack(int size) { + maxStack = size; + } + + /** + * Gets max_locals. + */ + public int getMaxLocals() { return maxLocals; } + + /** + * Sets max_locals. + */ + public void setMaxLocals(int size) { + maxLocals = size; + } + + /** + * Sets max_locals. + * + *

This computes the number of local variables + * used to pass method parameters and sets max_locals + * to that number plus locals. + * + * @param isStatic true if params must be + * interpreted as parameters to a static method. + * @param params parameter types. + * @param locals the number of local variables excluding + * ones used to pass parameters. + */ + public void setMaxLocals(boolean isStatic, CtClass[] params, + int locals) { + if (!isStatic) + ++locals; + + if (params != null) { + CtClass doubleType = CtClass.doubleType; + CtClass longType = CtClass.longType; + int n = params.length; + for (int i = 0; i < n; ++i) { + CtClass type = params[i]; + if (type == doubleType || type == longType) + locals += 2; + else + ++locals; + } + } + + maxLocals = locals; + } + + /** + * Increments max_locals. + */ + public void incMaxLocals(int diff) { + maxLocals += diff; + } + + /** + * Adds a new entry of exception_table. + */ + public void addExceptionHandler(int start, int end, + int handler, CtClass type) { + addExceptionHandler(start, end, handler, + constPool.addClassInfo(type)); + } + + /** + * Adds a new entry of exception_table. + * + * @param type the fully-qualified name of a throwable class. + */ + public void addExceptionHandler(int start, int end, + int handler, String type) { + addExceptionHandler(start, end, handler, + constPool.addClassInfo(type)); + } + + /** + * Adds a new entry of exception_table. + */ + public void addExceptionHandler(int start, int end, + int handler, int type) { + tryblocks.add(start, end, handler, type); + } + + /** + * Returns the length of bytecode sequence + * that have been added so far. + */ + public int currentPc() { + return getSize(); + } + + /** + * Reads a signed 8bit value at the offset from the beginning of the + * bytecode sequence. + * + * @throws ArrayIndexOutOfBoundsException if offset is invalid. + */ + public int read(int offset) { + return super.read(offset); + } + + /** + * Reads a signed 16bit value at the offset from the beginning of the + * bytecode sequence. + */ + public int read16bit(int offset) { + int v1 = read(offset); + int v2 = read(offset + 1); + return (v1 << 8) + (v2 & 0xff); + } + + /** + * Reads a signed 32bit value at the offset from the beginning of the + * bytecode sequence. + */ + public int read32bit(int offset) { + int v1 = read16bit(offset); + int v2 = read16bit(offset + 2); + return (v1 << 16) + (v2 & 0xffff); + } + + /** + * Writes an 8bit value at the offset from the beginning of the + * bytecode sequence. + * + * @throws ArrayIndexOutOfBoundsException if offset is invalid. + */ + public void write(int offset, int value) { + super.write(offset, value); + } + + /** + * Writes an 16bit value at the offset from the beginning of the + * bytecode sequence. + */ + public void write16bit(int offset, int value) { + write(offset, value >> 8); + write(offset + 1, value); + } + + /** + * Writes an 32bit value at the offset from the beginning of the + * bytecode sequence. + */ + public void write32bit(int offset, int value) { + write16bit(offset, value >> 16); + write16bit(offset + 2, value); + } + + /** + * Appends an 8bit value to the end of the bytecode sequence. + */ + public void add(int code) { + super.add(code); + } + + /** + * Appends a 32bit value to the end of the bytecode sequence. + */ + public void add32bit(int value) { + add(value >> 24, value >> 16, value >> 8, value); + } + + /** + * Appends the length-byte gap to the end of the bytecode sequence. + * + * @param length the gap length in byte. + */ + public void addGap(int length) { + super.addGap(length); + } + + /** + * Appends an 8bit opcode to the end of the bytecode sequence. + * The current stack depth is updated. + * max_stack is updated if the current stack depth + * is the deepest so far. + * + *

Note: some instructions such as INVOKEVIRTUAL does not + * update the current stack depth since the increment depends + * on the method signature. + * growStack() must be explicitly called. + */ + public void addOpcode(int code) { + add(code); + growStack(STACK_GROW[code]); + } + + /** + * Increases the current stack depth. + * It also updates max_stack if the current stack depth + * is the deepest so far. + * + * @param diff the number added to the current stack depth. + */ + public void growStack(int diff) { + setStackDepth(stackDepth + diff); + } + + /** + * Returns the current stack depth. + */ + public int getStackDepth() { return stackDepth; } + + /** + * Sets the current stack depth. + * It also updates max_stack if the current stack depth + * is the deepest so far. + * + * @param depth new value. + */ + public void setStackDepth(int depth) { + stackDepth = depth; + if (stackDepth > maxStack) + maxStack = stackDepth; + } + + /** + * Appends a 16bit value to the end of the bytecode sequence. + * It never changes the current stack depth. + */ + public void addIndex(int index) { + add(index >> 8, index); + } + + /** + * Appends ALOAD or (WIDE) ALOAD_<n> + * + * @param n an index into the local variable array. + */ + public void addAload(int n) { + if (n < 4) + addOpcode(42 + n); // aload_ + else if (n < 0x100) { + addOpcode(ALOAD); // aload + add(n); + } + else { + addOpcode(WIDE); + addOpcode(ALOAD); + addIndex(n); + } + } + + /** + * Appends ASTORE or (WIDE) ASTORE_<n> + * + * @param n an index into the local variable array. + */ + public void addAstore(int n) { + if (n < 4) + addOpcode(75 + n); // astore_ + else if (n < 0x100) { + addOpcode(ASTORE); // astore + add(n); + } + else { + addOpcode(WIDE); + addOpcode(ASTORE); + addIndex(n); + } + } + + /** + * Appends ICONST or ICONST_<n> + * + * @param n the pushed integer constant. + */ + public void addIconst(int n) { + if (n < 6 && -2 < n) + addOpcode(3 + n); // iconst_ -1..5 + else if (n <= 127 && -128 <= n) { + addOpcode(16); // bipush + add(n); + } + else if (n <= 32767 && -32768 <= n) { + addOpcode(17); // sipush + add(n >> 8); + add(n); + } + else + addLdc(constPool.addIntegerInfo(n)); + } + + /** + * Appends an instruction for pushing zero or null on the stack. + * If the type is void, this method does not append any instruction. + * + * @param type the type of the zero value (or null). + */ + public void addConstZero(CtClass type) { + if (type.isPrimitive()) { + if (type == CtClass.longType) + addOpcode(LCONST_0); + else if (type == CtClass.floatType) + addOpcode(FCONST_0); + else if (type == CtClass.doubleType) + addOpcode(DCONST_0); + else if (type == CtClass.voidType) + throw new RuntimeException("void type?"); + else + addOpcode(ICONST_0); + } + else + addOpcode(ACONST_NULL); + } + + /** + * Appends ILOAD or (WIDE) ILOAD_<n> + * + * @param n an index into the local variable array. + */ + public void addIload(int n) { + if (n < 4) + addOpcode(26 + n); // iload_ + else if (n < 0x100) { + addOpcode(ILOAD); // iload + add(n); + } + else { + addOpcode(WIDE); + addOpcode(ILOAD); + addIndex(n); + } + } + + /** + * Appends ISTORE or (WIDE) ISTORE_<n> + * + * @param n an index into the local variable array. + */ + public void addIstore(int n) { + if (n < 4) + addOpcode(59 + n); // istore_ + else if (n < 0x100) { + addOpcode(ISTORE); // istore + add(n); + } + else { + addOpcode(WIDE); + addOpcode(ISTORE); + addIndex(n); + } + } + + /** + * Appends LCONST or LCONST_<n> + * + * @param n the pushed long integer constant. + */ + public void addLconst(long n) { + if (n == 0 || n == 1) + addOpcode(9 + (int)n); // lconst_ + else + addLdc2w(n); + } + + /** + * Appends LLOAD or (WIDE) LLOAD_<n> + * + * @param n an index into the local variable array. + */ + public void addLload(int n) { + if (n < 4) + addOpcode(30 + n); // lload_ + else if (n < 0x100) { + addOpcode(LLOAD); // lload + add(n); + } + else { + addOpcode(WIDE); + addOpcode(LLOAD); + addIndex(n); + } + } + + /** + * Appends LSTORE or LSTORE_<n> + * + * @param n an index into the local variable array. + */ + public void addLstore(int n) { + if (n < 4) + addOpcode(63 + n); // lstore_ + else if (n < 0x100) { + addOpcode(LSTORE); // lstore + add(n); + } + else { + addOpcode(WIDE); + addOpcode(LSTORE); + addIndex(n); + } + } + + /** + * Appends DCONST or DCONST_<n> + * + * @param d the pushed double constant. + */ + public void addDconst(double d) { + if (d == 0.0 || d == 1.0) + addOpcode(14 + (int)d); // dconst_ + else + addLdc2w(d); + } + + /** + * Appends DLOAD or (WIDE) DLOAD_<n> + * + * @param n an index into the local variable array. + */ + public void addDload(int n) { + if (n < 4) + addOpcode(38 + n); // dload_ + else if (n < 0x100) { + addOpcode(DLOAD); // dload + add(n); + } + else { + addOpcode(WIDE); + addOpcode(DLOAD); + addIndex(n); + } + } + + /** + * Appends DSTORE or (WIDE) DSTORE_<n> + * + * @param n an index into the local variable array. + */ + public void addDstore(int n) { + if (n < 4) + addOpcode(71 + n); // dstore_ + else if (n < 0x100) { + addOpcode(DSTORE); // dstore + add(n); + } + else { + addOpcode(WIDE); + addOpcode(DSTORE); + addIndex(n); + } + } + + /** + * Appends FCONST or FCONST_<n> + * + * @param f the pushed float constant. + */ + public void addFconst(float f) { + if (f == 0.0f || f == 1.0f || f == 2.0f) + addOpcode(11 + (int)f); // fconst_ + else + addLdc(constPool.addFloatInfo(f)); + } + + /** + * Appends FLOAD or (WIDE) FLOAD_<n> + * + * @param n an index into the local variable array. + */ + public void addFload(int n) { + if (n < 4) + addOpcode(34 + n); // fload_ + else if (n < 0x100) { + addOpcode(FLOAD); // fload + add(n); + } + else { + addOpcode(WIDE); + addOpcode(FLOAD); + addIndex(n); + } + } + + /** + * Appends FSTORE or FSTORE_<n> + * + * @param n an index into the local variable array. + */ + public void addFstore(int n) { + if (n < 4) + addOpcode(67 + n); // fstore_ + else if (n < 0x100) { + addOpcode(FSTORE); // fstore + add(n); + } + else { + addOpcode(WIDE); + addOpcode(FSTORE); + addIndex(n); + } + } + + /** + * Appends an instruction for loading a value from the + * local variable at the index n. + * + * @param n the index. + * @param type the type of the loaded value. + * @return the size of the value (1 or 2 word). + */ + public int addLoad(int n, CtClass type) { + if (type.isPrimitive()) { + if (type == CtClass.booleanType || type == CtClass.charType + || type == CtClass.byteType || type == CtClass.shortType + || type == CtClass.intType) + addIload(n); + else if (type == CtClass.longType) { + addLload(n); + return 2; + } + else if(type == CtClass.floatType) + addFload(n); + else if(type == CtClass.doubleType) { + addDload(n); + return 2; + } + else + throw new RuntimeException("void type?"); + } + else + addAload(n); + + return 1; + } + + /** + * Appends an instruction for storing a value into the + * local variable at the index n. + * + * @param n the index. + * @param type the type of the stored value. + * @return 2 if the type is long or double. Otherwise 1. + */ + public int addStore(int n, CtClass type) { + if (type.isPrimitive()) { + if (type == CtClass.booleanType || type == CtClass.charType + || type == CtClass.byteType || type == CtClass.shortType + || type == CtClass.intType) + addIstore(n); + else if (type == CtClass.longType) { + addLstore(n); + return 2; + } + else if (type == CtClass.floatType) + addFstore(n); + else if (type == CtClass.doubleType) { + addDstore(n); + return 2; + } + else + throw new RuntimeException("void type?"); + } + else + addAstore(n); + + return 1; + } + + /** + * Appends instructions for loading all the parameters onto the + * operand stack. + * + * @param offset the index of the first parameter. It is 0 + * if the method is static. Otherwise, it is 1. + */ + public int addLoadParameters(CtClass[] params, int offset) { + int stacksize = 0; + if (params != null) { + int n = params.length; + for (int i = 0; i < n; ++i) + stacksize += addLoad(stacksize + offset, params[i]); + } + + return stacksize; + } + + /** + * Appends CHECKCAST. + * + * @param c the type. + */ + public void addCheckcast(CtClass c) { + addOpcode(CHECKCAST); + addIndex(constPool.addClassInfo(c)); + } + + /** + * Appends CHECKCAST. + * + * @param classname a fully-qualified class name. + */ + public void addCheckcast(String classname) { + addOpcode(CHECKCAST); + addIndex(constPool.addClassInfo(classname)); + } + + /** + * Appends INSTANCEOF. + * + * @param classname the class name. + */ + public void addInstanceof(String classname) { + addOpcode(INSTANCEOF); + addIndex(constPool.addClassInfo(classname)); + } + + /** + * Appends GETFIELD. + * + * @param c the class. + * @param name the field name. + * @param type the descriptor of the field type. + * + * @see Descriptor#of(CtClass) + */ + public void addGetfield(CtClass c, String name, String type) { + add(GETFIELD); + int ci = constPool.addClassInfo(c); + addIndex(constPool.addFieldrefInfo(ci, name, type)); + growStack(Descriptor.dataSize(type) - 1); + } + + /** + * Appends GETFIELD. + * + * @param c the fully-qualified class name. + * @param name the field name. + * @param type the descriptor of the field type. + * + * @see Descriptor#of(CtClass) + */ + public void addGetfield(String c, String name, String type) { + add(GETFIELD); + int ci = constPool.addClassInfo(c); + addIndex(constPool.addFieldrefInfo(ci, name, type)); + growStack(Descriptor.dataSize(type) - 1); + } + + /** + * Appends GETSTATIC. + * + * @param c the class + * @param name the field name + * @param type the descriptor of the field type. + * + * @see Descriptor#of(CtClass) + */ + public void addGetstatic(CtClass c, String name, String type) { + add(GETSTATIC); + int ci = constPool.addClassInfo(c); + addIndex(constPool.addFieldrefInfo(ci, name, type)); + growStack(Descriptor.dataSize(type)); + } + + /** + * Appends GETSTATIC. + * + * @param c the fully-qualified class name + * @param name the field name + * @param type the descriptor of the field type. + * + * @see Descriptor#of(CtClass) + */ + public void addGetstatic(String c, String name, String type) { + add(GETSTATIC); + int ci = constPool.addClassInfo(c); + addIndex(constPool.addFieldrefInfo(ci, name, type)); + growStack(Descriptor.dataSize(type)); + } + + /** + * Appends INVOKESPECIAL. + * + * @param clazz the target class. + * @param name the method name. + * @param returnType the return type. + * @param paramTypes the parameter types. + */ + public void addInvokespecial(CtClass clazz, String name, + CtClass returnType, CtClass[] paramTypes) { + String desc = Descriptor.ofMethod(returnType, paramTypes); + addInvokespecial(clazz, name, desc); + } + + /** + * Appends INVOKESPECIAL. + * + * @param clazz the target class. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + * @see Descriptor#ofConstructor(CtClass[]) + */ + public void addInvokespecial(CtClass clazz, String name, String desc) { + boolean isInterface = clazz == null ? false : clazz.isInterface(); + addInvokespecial(isInterface, + constPool.addClassInfo(clazz), name, desc); + } + + /** + * Appends INVOKESPECIAL. The invoked method must not be a default + * method declared in an interface. + * + * @param clazz the fully-qualified class name. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + * @see Descriptor#ofConstructor(CtClass[]) + */ + public void addInvokespecial(String clazz, String name, String desc) { + addInvokespecial(false, constPool.addClassInfo(clazz), name, desc); + } + + /** + * Appends INVOKESPECIAL. The invoked method must not be a default + * method declared in an interface. + * + * @param clazz the index of CONSTANT_Class_info + * structure. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + * @see Descriptor#ofConstructor(CtClass[]) + */ + public void addInvokespecial(int clazz, String name, String desc) { + addInvokespecial(false, clazz, name, desc); + } + + /** + * Appends INVOKESPECIAL. + * + * @param isInterface true if the invoked method is a default method + * declared in an interface. + * @param clazz the index of CONSTANT_Class_info + * structure. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + * @see Descriptor#ofConstructor(CtClass[]) + */ + public void addInvokespecial(boolean isInterface, int clazz, String name, String desc) { + add(INVOKESPECIAL); + int index; + if (isInterface) + index = constPool.addInterfaceMethodrefInfo(clazz, name, desc); + else + index = constPool.addMethodrefInfo(clazz, name, desc); + + addIndex(index); + growStack(Descriptor.dataSize(desc) - 1); + } + + /** + * Appends INVOKESTATIC. + * + * @param clazz the target class. + * @param name the method name + * @param returnType the return type. + * @param paramTypes the parameter types. + */ + public void addInvokestatic(CtClass clazz, String name, + CtClass returnType, CtClass[] paramTypes) { + String desc = Descriptor.ofMethod(returnType, paramTypes); + addInvokestatic(clazz, name, desc); + } + + /** + * Appends INVOKESTATIC. + * + * @param clazz the target class. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + */ + public void addInvokestatic(CtClass clazz, String name, String desc) { + addInvokestatic(constPool.addClassInfo(clazz), name, desc); + } + + /** + * Appends INVOKESTATIC. + * + * @param classname the fully-qualified class name. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + */ + public void addInvokestatic(String classname, String name, String desc) { + addInvokestatic(constPool.addClassInfo(classname), name, desc); + } + + /** + * Appends INVOKESTATIC. + * + * @param clazz the index of CONSTANT_Class_info + * structure. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + */ + public void addInvokestatic(int clazz, String name, String desc) { + add(INVOKESTATIC); + addIndex(constPool.addMethodrefInfo(clazz, name, desc)); + growStack(Descriptor.dataSize(desc)); + } + + /** + * Appends INVOKEVIRTUAL. + * + *

The specified method must not be an inherited method. + * It must be directly declared in the class specified + * in clazz. + * + * @param clazz the target class. + * @param name the method name + * @param returnType the return type. + * @param paramTypes the parameter types. + */ + public void addInvokevirtual(CtClass clazz, String name, + CtClass returnType, CtClass[] paramTypes) { + String desc = Descriptor.ofMethod(returnType, paramTypes); + addInvokevirtual(clazz, name, desc); + } + + /** + * Appends INVOKEVIRTUAL. + * + *

The specified method must not be an inherited method. + * It must be directly declared in the class specified + * in clazz. + * + * @param clazz the target class. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + */ + public void addInvokevirtual(CtClass clazz, String name, String desc) { + addInvokevirtual(constPool.addClassInfo(clazz), name, desc); + } + + /** + * Appends INVOKEVIRTUAL. + * + *

The specified method must not be an inherited method. + * It must be directly declared in the class specified + * in classname. + * + * @param classname the fully-qualified class name. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + */ + public void addInvokevirtual(String classname, String name, String desc) { + addInvokevirtual(constPool.addClassInfo(classname), name, desc); + } + + /** + * Appends INVOKEVIRTUAL. + * + *

The specified method must not be an inherited method. + * It must be directly declared in the class specified + * by clazz. + * + * @param clazz the index of CONSTANT_Class_info + * structure. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + */ + public void addInvokevirtual(int clazz, String name, String desc) { + add(INVOKEVIRTUAL); + addIndex(constPool.addMethodrefInfo(clazz, name, desc)); + growStack(Descriptor.dataSize(desc) - 1); + } + + /** + * Appends INVOKEINTERFACE. + * + * @param clazz the target class. + * @param name the method name + * @param returnType the return type. + * @param paramTypes the parameter types. + * @param count the count operand of the instruction. + */ + public void addInvokeinterface(CtClass clazz, String name, + CtClass returnType, CtClass[] paramTypes, + int count) { + String desc = Descriptor.ofMethod(returnType, paramTypes); + addInvokeinterface(clazz, name, desc, count); + } + + /** + * Appends INVOKEINTERFACE. + * + * @param clazz the target class. + * @param name the method name + * @param desc the descriptor of the method signature. + * @param count the count operand of the instruction. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + */ + public void addInvokeinterface(CtClass clazz, String name, + String desc, int count) { + addInvokeinterface(constPool.addClassInfo(clazz), name, desc, + count); + } + + /** + * Appends INVOKEINTERFACE. + * + * @param classname the fully-qualified class name. + * @param name the method name + * @param desc the descriptor of the method signature. + * @param count the count operand of the instruction. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + */ + public void addInvokeinterface(String classname, String name, + String desc, int count) { + addInvokeinterface(constPool.addClassInfo(classname), name, desc, + count); + } + + /** + * Appends INVOKEINTERFACE. + * + * @param clazz the index of CONSTANT_Class_info + * structure. + * @param name the method name + * @param desc the descriptor of the method signature. + * @param count the count operand of the instruction. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + */ + public void addInvokeinterface(int clazz, String name, + String desc, int count) { + add(INVOKEINTERFACE); + addIndex(constPool.addInterfaceMethodrefInfo(clazz, name, desc)); + add(count); + add(0); + growStack(Descriptor.dataSize(desc) - 1); + } + + /** + * Appends INVOKEDYNAMIC. + * + * @param bootstrap an index into the bootstrap_methods array + * of the bootstrap method table. + * @param name the method name. + * @param desc the method descriptor. + * @see Descriptor#ofMethod(CtClass,CtClass[]) + * @since 3.17 + */ + public void addInvokedynamic(int bootstrap, String name, String desc) { + int nt = constPool.addNameAndTypeInfo(name, desc); + int dyn = constPool.addInvokeDynamicInfo(bootstrap, nt); + add(INVOKEDYNAMIC); + addIndex(dyn); + add(0, 0); + growStack(Descriptor.dataSize(desc)); // assume ConstPool#REF_invokeStatic + } + + /** + * Appends LDC or LDC_W. The pushed item is a String + * object. + * + * @param s the character string pushed by LDC or LDC_W. + */ + public void addLdc(String s) { + addLdc(constPool.addStringInfo(s)); + } + + /** + * Appends LDC or LDC_W. + * + * @param i index into the constant pool. + */ + public void addLdc(int i) { + if (i > 0xFF) { + addOpcode(LDC_W); + addIndex(i); + } + else { + addOpcode(LDC); + add(i); + } + } + + /** + * Appends LDC2_W. The pushed item is a long value. + */ + public void addLdc2w(long l) { + addOpcode(LDC2_W); + addIndex(constPool.addLongInfo(l)); + } + + /** + * Appends LDC2_W. The pushed item is a double value. + */ + public void addLdc2w(double d) { + addOpcode(LDC2_W); + addIndex(constPool.addDoubleInfo(d)); + } + + /** + * Appends NEW. + * + * @param clazz the class of the created instance. + */ + public void addNew(CtClass clazz) { + addOpcode(NEW); + addIndex(constPool.addClassInfo(clazz)); + } + + /** + * Appends NEW. + * + * @param classname the fully-qualified class name. + */ + public void addNew(String classname) { + addOpcode(NEW); + addIndex(constPool.addClassInfo(classname)); + } + + /** + * Appends ANEWARRAY. + * + * @param classname the qualified class name of the element type. + */ + public void addAnewarray(String classname) { + addOpcode(ANEWARRAY); + addIndex(constPool.addClassInfo(classname)); + } + + /** + * Appends ICONST and ANEWARRAY. + * + * @param clazz the elememnt type. + * @param length the array length. + */ + public void addAnewarray(CtClass clazz, int length) { + addIconst(length); + addOpcode(ANEWARRAY); + addIndex(constPool.addClassInfo(clazz)); + } + + /** + * Appends NEWARRAY for primitive types. + * + * @param atype T_BOOLEAN, T_CHAR, ... + * @see Opcode + */ + public void addNewarray(int atype, int length) { + addIconst(length); + addOpcode(NEWARRAY); + add(atype); + } + + /** + * Appends MULTINEWARRAY. + * + * @param clazz the array type. + * @param dimensions the sizes of all dimensions. + * @return the length of dimensions. + */ + public int addMultiNewarray(CtClass clazz, int[] dimensions) { + int len = dimensions.length; + for (int i = 0; i < len; ++i) + addIconst(dimensions[i]); + + growStack(len); + return addMultiNewarray(clazz, len); + } + + /** + * Appends MULTINEWARRAY. The size of every dimension must have been + * already pushed on the stack. + * + * @param clazz the array type. + * @param dim the number of the dimensions. + * @return the value of dim. + */ + public int addMultiNewarray(CtClass clazz, int dim) { + add(MULTIANEWARRAY); + addIndex(constPool.addClassInfo(clazz)); + add(dim); + growStack(1 - dim); + return dim; + } + + /** + * Appends MULTINEWARRAY. + * + * @param desc the type descriptor of the created array. + * @param dim dimensions. + * @return the value of dim. + */ + public int addMultiNewarray(String desc, int dim) { + add(MULTIANEWARRAY); + addIndex(constPool.addClassInfo(desc)); + add(dim); + growStack(1 - dim); + return dim; + } + + /** + * Appends PUTFIELD. + * + * @param c the target class. + * @param name the field name. + * @param desc the descriptor of the field type. + */ + public void addPutfield(CtClass c, String name, String desc) { + addPutfield0(c, null, name, desc); + } + + /** + * Appends PUTFIELD. + * + * @param classname the fully-qualified name of the target class. + * @param name the field name. + * @param desc the descriptor of the field type. + */ + public void addPutfield(String classname, String name, String desc) { + // if classnaem is null, the target class is THIS. + addPutfield0(null, classname, name, desc); + } + + private void addPutfield0(CtClass target, String classname, + String name, String desc) { + add(PUTFIELD); + // target is null if it represents THIS. + int ci = classname == null ? constPool.addClassInfo(target) + : constPool.addClassInfo(classname); + addIndex(constPool.addFieldrefInfo(ci, name, desc)); + growStack(-1 - Descriptor.dataSize(desc)); + } + + /** + * Appends PUTSTATIC. + * + * @param c the target class. + * @param name the field name. + * @param desc the descriptor of the field type. + */ + public void addPutstatic(CtClass c, String name, String desc) { + addPutstatic0(c, null, name, desc); + } + + /** + * Appends PUTSTATIC. + * + * @param classname the fully-qualified name of the target class. + * @param fieldName the field name. + * @param desc the descriptor of the field type. + */ + public void addPutstatic(String classname, String fieldName, String desc) { + // if classname is null, the target class is THIS. + addPutstatic0(null, classname, fieldName, desc); + } + + private void addPutstatic0(CtClass target, String classname, + String fieldName, String desc) { + add(PUTSTATIC); + // target is null if it represents THIS. + int ci = classname == null ? constPool.addClassInfo(target) + : constPool.addClassInfo(classname); + addIndex(constPool.addFieldrefInfo(ci, fieldName, desc)); + growStack(-Descriptor.dataSize(desc)); + } + + /** + * Appends ARETURN, IRETURN, .., or RETURN. + * + * @param type the return type. + */ + public void addReturn(CtClass type) { + if (type == null) + addOpcode(RETURN); + else if (type.isPrimitive()) { + CtPrimitiveType ptype = (CtPrimitiveType)type; + addOpcode(ptype.getReturnOp()); + } + else + addOpcode(ARETURN); + } + + /** + * Appends RET. + * + * @param var local variable + */ + public void addRet(int var) { + if (var < 0x100) { + addOpcode(RET); + add(var); + } + else { + addOpcode(WIDE); + addOpcode(RET); + addIndex(var); + } + } + + /** + * Appends instructions for executing + * java.lang.System.println(message). + * + * @param message printed message. + */ + public void addPrintln(String message) { + addGetstatic("java.lang.System", "err", "Ljava/io/PrintStream;"); + addLdc(message); + addInvokevirtual("java.io.PrintStream", + "println", "(Ljava/lang/String;)V"); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFile.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFile.java new file mode 100644 index 0000000..5a6a70c --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFile.java @@ -0,0 +1,932 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import com.wenshuo.agent.javassist.CannotCompileException; + +/** + * ClassFile represents a Java .class file, which + * consists of a constant pool, methods, fields, and attributes. + * + *

For example,

+ *
+ * ClassFile cf = new ClassFile(false, "test.Foo", null);
+ * cf.setInterfaces(new String[] { "java.lang.Cloneable" });
+ *
+ * FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I");
+ * f.setAccessFlags(AccessFlag.PUBLIC);
+ * cf.addField(f);
+ *
+ * cf.write(new DataOutputStream(new FileOutputStream("Foo.class")));
+ * 
+ *

This code generates a class file Foo.class for the following class:

+ *
+ * package test;
+ * class Foo implements Cloneable {
+ *     public int width;
+ * }
+ * 
+ * + * @see FieldInfo + * @see MethodInfo + * @see ClassFileWriter + * @see javassist.CtClass#getClassFile() + * @see javassist.ClassPool#makeClass(ClassFile) + */ +public final class ClassFile { + int major, minor; // version number + ConstPool constPool; + int thisClass; + int accessFlags; + int superClass; + int[] interfaces; + ArrayList fields; + ArrayList methods; + ArrayList attributes; + String thisclassname; // not JVM-internal name + String[] cachedInterfaces; + String cachedSuperclass; + + /** + * The major version number of class files + * for JDK 1.1. + */ + public static final int JAVA_1 = 45; + + /** + * The major version number of class files + * for JDK 1.2. + */ + public static final int JAVA_2 = 46; + + /** + * The major version number of class files + * for JDK 1.3. + */ + public static final int JAVA_3 = 47; + + /** + * The major version number of class files + * for JDK 1.4. + */ + public static final int JAVA_4 = 48; + + /** + * The major version number of class files + * for JDK 1.5. + */ + public static final int JAVA_5 = 49; + + /** + * The major version number of class files + * for JDK 1.6. + */ + public static final int JAVA_6 = 50; + + /** + * The major version number of class files + * for JDK 1.7. + */ + public static final int JAVA_7 = 51; + + /** + * The major version number of class files + * for JDK 1.8. + */ + public static final int JAVA_8 = 52; + + /** + * The major version number of class files created + * from scratch. The default value is 47 (JDK 1.3). + * It is 49 (JDK 1.5) + * if the JVM supports java.lang.StringBuilder. + * It is 50 (JDK 1.6) + * if the JVM supports java.util.zip.DeflaterInputStream. + * It is 51 (JDK 1.7) + * if the JVM supports java.lang.invoke.CallSite. + */ + public static int MAJOR_VERSION = JAVA_3; + + static { + try { + Class.forName("java.lang.StringBuilder"); + MAJOR_VERSION = JAVA_5; + Class.forName("java.util.zip.DeflaterInputStream"); + MAJOR_VERSION = JAVA_6; + Class.forName("java.lang.invoke.CallSite"); + MAJOR_VERSION = JAVA_7; + } + catch (Throwable t) {} + } + + /** + * Constructs a class file from a byte stream. + */ + public ClassFile(DataInputStream in) throws IOException { + read(in); + } + + /** + * Constructs a class file including no members. + * + * @param isInterface + * true if this is an interface. false if this is a class. + * @param classname + * a fully-qualified class name + * @param superclass + * a fully-qualified super class name or null. + */ + public ClassFile(boolean isInterface, String classname, String superclass) { + major = MAJOR_VERSION; + minor = 0; // JDK 1.3 or later + constPool = new ConstPool(classname); + thisClass = constPool.getThisClassInfo(); + if (isInterface) + accessFlags = AccessFlag.INTERFACE | AccessFlag.ABSTRACT; + else + accessFlags = AccessFlag.SUPER; + + initSuperclass(superclass); + interfaces = null; + fields = new ArrayList(); + methods = new ArrayList(); + thisclassname = classname; + + attributes = new ArrayList(); + attributes.add(new SourceFileAttribute(constPool, + getSourcefileName(thisclassname))); + } + + private void initSuperclass(String superclass) { + if (superclass != null) { + this.superClass = constPool.addClassInfo(superclass); + cachedSuperclass = superclass; + } + else { + this.superClass = constPool.addClassInfo("java.lang.Object"); + cachedSuperclass = "java.lang.Object"; + } + } + + private static String getSourcefileName(String qname) { + int index = qname.lastIndexOf('.'); + if (index >= 0) + qname = qname.substring(index + 1); + + return qname + ".java"; + } + + /** + * Eliminates dead constant pool items. If a method or a field is removed, + * the constant pool items used by that method/field become dead items. This + * method recreates a constant pool. + */ + public void compact() { + ConstPool cp = compact0(); + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + minfo.compact(cp); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + finfo.compact(cp); + } + + attributes = AttributeInfo.copyAll(attributes, cp); + constPool = cp; + } + + private ConstPool compact0() { + ConstPool cp = new ConstPool(thisclassname); + thisClass = cp.getThisClassInfo(); + String sc = getSuperclass(); + if (sc != null) + superClass = cp.addClassInfo(getSuperclass()); + + if (interfaces != null) { + int n = interfaces.length; + for (int i = 0; i < n; ++i) + interfaces[i] + = cp.addClassInfo(constPool.getClassInfo(interfaces[i])); + } + + return cp; + } + + /** + * Discards all attributes, associated with both the class file and the + * members such as a code attribute and exceptions attribute. The unused + * constant pool entries are also discarded (a new packed constant pool is + * constructed). + */ + public void prune() { + ConstPool cp = compact0(); + ArrayList newAttributes = new ArrayList(); + AttributeInfo invisibleAnnotations + = getAttribute(AnnotationsAttribute.invisibleTag); + if (invisibleAnnotations != null) { + invisibleAnnotations = invisibleAnnotations.copy(cp, null); + newAttributes.add(invisibleAnnotations); + } + + AttributeInfo visibleAnnotations + = getAttribute(AnnotationsAttribute.visibleTag); + if (visibleAnnotations != null) { + visibleAnnotations = visibleAnnotations.copy(cp, null); + newAttributes.add(visibleAnnotations); + } + + AttributeInfo signature + = getAttribute(SignatureAttribute.tag); + if (signature != null) { + signature = signature.copy(cp, null); + newAttributes.add(signature); + } + + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + minfo.prune(cp); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + finfo.prune(cp); + } + + attributes = newAttributes; + constPool = cp; + } + + /** + * Returns a constant pool table. + */ + public ConstPool getConstPool() { + return constPool; + } + + /** + * Returns true if this is an interface. + */ + public boolean isInterface() { + return (accessFlags & AccessFlag.INTERFACE) != 0; + } + + /** + * Returns true if this is a final class or interface. + */ + public boolean isFinal() { + return (accessFlags & AccessFlag.FINAL) != 0; + } + + /** + * Returns true if this is an abstract class or an interface. + */ + public boolean isAbstract() { + return (accessFlags & AccessFlag.ABSTRACT) != 0; + } + + /** + * Returns access flags. + * + * @see javassist.bytecode.AccessFlag + */ + public int getAccessFlags() { + return accessFlags; + } + + /** + * Changes access flags. + * + * @see javassist.bytecode.AccessFlag + */ + public void setAccessFlags(int acc) { + if ((acc & AccessFlag.INTERFACE) == 0) + acc |= AccessFlag.SUPER; + + accessFlags = acc; + } + + /** + * Returns access and property flags of this nested class. + * This method returns -1 if the class is not a nested class. + * + *

The returned value is obtained from inner_class_access_flags + * of the entry representing this nested class itself + * in InnerClasses_attribute. + */ + public int getInnerAccessFlags() { + InnerClassesAttribute ica + = (InnerClassesAttribute)getAttribute(InnerClassesAttribute.tag); + if (ica == null) + return -1; + + String name = getName(); + int n = ica.tableLength(); + for (int i = 0; i < n; ++i) + if (name.equals(ica.innerClass(i))) + return ica.accessFlags(i); + + return -1; + } + + /** + * Returns the class name. + */ + public String getName() { + return thisclassname; + } + + /** + * Sets the class name. This method substitutes the new name for all + * occurrences of the old class name in the class file. + */ + public void setName(String name) { + renameClass(thisclassname, name); + } + + /** + * Returns the super class name. + */ + public String getSuperclass() { + if (cachedSuperclass == null) + cachedSuperclass = constPool.getClassInfo(superClass); + + return cachedSuperclass; + } + + /** + * Returns the index of the constant pool entry representing the super + * class. + */ + public int getSuperclassId() { + return superClass; + } + + /** + * Sets the super class. + * + *

+ * The new super class should inherit from the old super class. + * This method modifies constructors so that they call constructors declared + * in the new super class. + */ + public void setSuperclass(String superclass) throws CannotCompileException { + if (superclass == null) + superclass = "java.lang.Object"; + + try { + this.superClass = constPool.addClassInfo(superclass); + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + minfo.setSuperclass(superclass); + } + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + cachedSuperclass = superclass; + } + + /** + * Replaces all occurrences of a class name in the class file. + * + *

+ * If class X is substituted for class Y in the class file, X and Y must + * have the same signature. If Y provides a method m(), X must provide it + * even if X inherits m() from the super class. If this fact is not + * guaranteed, the bytecode verifier may cause an error. + * + * @param oldname + * the replaced class name + * @param newname + * the substituted class name + */ + public final void renameClass(String oldname, String newname) { + ArrayList list; + int n; + + if (oldname.equals(newname)) + return; + + if (oldname.equals(thisclassname)) + thisclassname = newname; + + oldname = Descriptor.toJvmName(oldname); + newname = Descriptor.toJvmName(newname); + constPool.renameClass(oldname, newname); + + AttributeInfo.renameClass(attributes, oldname, newname); + list = methods; + n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + String desc = minfo.getDescriptor(); + minfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); + AttributeInfo.renameClass(minfo.getAttributes(), oldname, newname); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + String desc = finfo.getDescriptor(); + finfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); + AttributeInfo.renameClass(finfo.getAttributes(), oldname, newname); + } + } + + /** + * Replaces all occurrences of several class names in the class file. + * + * @param classnames + * specifies which class name is replaced with which new name. + * Class names must be described with the JVM-internal + * representation like java/lang/Object. + * @see #renameClass(String,String) + */ + public final void renameClass(Map classnames) { + String jvmNewThisName = (String)classnames.get(Descriptor + .toJvmName(thisclassname)); + if (jvmNewThisName != null) + thisclassname = Descriptor.toJavaName(jvmNewThisName); + + constPool.renameClass(classnames); + + AttributeInfo.renameClass(attributes, classnames); + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + String desc = minfo.getDescriptor(); + minfo.setDescriptor(Descriptor.rename(desc, classnames)); + AttributeInfo.renameClass(minfo.getAttributes(), classnames); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + String desc = finfo.getDescriptor(); + finfo.setDescriptor(Descriptor.rename(desc, classnames)); + AttributeInfo.renameClass(finfo.getAttributes(), classnames); + } + } + + /** + * Internal-use only. + * CtClass.getRefClasses() calls this method. + */ + public final void getRefClasses(Map classnames) { + constPool.renameClass(classnames); + + AttributeInfo.getRefClasses(attributes, classnames); + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + String desc = minfo.getDescriptor(); + Descriptor.rename(desc, classnames); + AttributeInfo.getRefClasses(minfo.getAttributes(), classnames); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + String desc = finfo.getDescriptor(); + Descriptor.rename(desc, classnames); + AttributeInfo.getRefClasses(finfo.getAttributes(), classnames); + } + } + + /** + * Returns the names of the interfaces implemented by the class. + * The returned array is read only. + */ + public String[] getInterfaces() { + if (cachedInterfaces != null) + return cachedInterfaces; + + String[] rtn = null; + if (interfaces == null) + rtn = new String[0]; + else { + int n = interfaces.length; + String[] list = new String[n]; + for (int i = 0; i < n; ++i) + list[i] = constPool.getClassInfo(interfaces[i]); + + rtn = list; + } + + cachedInterfaces = rtn; + return rtn; + } + + /** + * Sets the interfaces. + * + * @param nameList + * the names of the interfaces. + */ + public void setInterfaces(String[] nameList) { + cachedInterfaces = null; + if (nameList != null) { + int n = nameList.length; + interfaces = new int[n]; + for (int i = 0; i < n; ++i) + interfaces[i] = constPool.addClassInfo(nameList[i]); + } + } + + /** + * Appends an interface to the interfaces implemented by the class. + */ + public void addInterface(String name) { + cachedInterfaces = null; + int info = constPool.addClassInfo(name); + if (interfaces == null) { + interfaces = new int[1]; + interfaces[0] = info; + } + else { + int n = interfaces.length; + int[] newarray = new int[n + 1]; + System.arraycopy(interfaces, 0, newarray, 0, n); + newarray[n] = info; + interfaces = newarray; + } + } + + /** + * Returns all the fields declared in the class. + * + * @return a list of FieldInfo. + * @see FieldInfo + */ + public List getFields() { + return fields; + } + + /** + * Appends a field to the class. + * + * @throws DuplicateMemberException when the field is already included. + */ + public void addField(FieldInfo finfo) throws DuplicateMemberException { + testExistingField(finfo.getName(), finfo.getDescriptor()); + fields.add(finfo); + } + + /** + * Just appends a field to the class. + * It does not check field duplication. + * Use this method only when minimizing performance overheads + * is seriously required. + * + * @since 3.13 + */ + public final void addField2(FieldInfo finfo) { + fields.add(finfo); + } + + private void testExistingField(String name, String descriptor) + throws DuplicateMemberException { + ListIterator it = fields.listIterator(0); + while (it.hasNext()) { + FieldInfo minfo = (FieldInfo)it.next(); + if (minfo.getName().equals(name)) + throw new DuplicateMemberException("duplicate field: " + name); + } + } + + /** + * Returns all the methods declared in the class. + * + * @return a list of MethodInfo. + * @see MethodInfo + */ + public List getMethods() { + return methods; + } + + /** + * Returns the method with the specified name. If there are multiple methods + * with that name, this method returns one of them. + * + * @return null if no such method is found. + */ + public MethodInfo getMethod(String name) { + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + if (minfo.getName().equals(name)) + return minfo; + } + + return null; + } + + /** + * Returns a static initializer (class initializer), or null if it does not + * exist. + */ + public MethodInfo getStaticInitializer() { + return getMethod(MethodInfo.nameClinit); + } + + /** + * Appends a method to the class. + * If there is a bridge method with the same name and signature, + * then the bridge method is removed before a new method is added. + * + * @throws DuplicateMemberException when the method is already included. + */ + public void addMethod(MethodInfo minfo) throws DuplicateMemberException { + testExistingMethod(minfo); + methods.add(minfo); + } + + /** + * Just appends a method to the class. + * It does not check method duplication or remove a bridge method. + * Use this method only when minimizing performance overheads + * is seriously required. + * + * @since 3.13 + */ + public final void addMethod2(MethodInfo minfo) { + methods.add(minfo); + } + + private void testExistingMethod(MethodInfo newMinfo) + throws DuplicateMemberException + { + String name = newMinfo.getName(); + String descriptor = newMinfo.getDescriptor(); + ListIterator it = methods.listIterator(0); + while (it.hasNext()) + if (isDuplicated(newMinfo, name, descriptor, (MethodInfo)it.next(), it)) + throw new DuplicateMemberException("duplicate method: " + name + + " in " + this.getName()); + } + + private static boolean isDuplicated(MethodInfo newMethod, String newName, + String newDesc, MethodInfo minfo, + ListIterator it) + { + if (!minfo.getName().equals(newName)) + return false; + + String desc = minfo.getDescriptor(); + if (!Descriptor.eqParamTypes(desc, newDesc)) + return false; + + if (desc.equals(newDesc)) { + if (notBridgeMethod(minfo)) + return true; + else { + // if the bridge method with the same signature + // already exists, replace it. + it.remove(); + return false; + } + } + else + return false; + // return notBridgeMethod(minfo) && notBridgeMethod(newMethod); + } + + /* For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed. + */ + private static boolean notBridgeMethod(MethodInfo minfo) { + return (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0; + } + + /** + * Returns all the attributes. The returned List object + * is shared with this object. If you add a new attribute to the list, + * the attribute is also added to the classs file represented by this + * object. If you remove an attribute from the list, it is also removed + * from the class file. + * + * @return a list of AttributeInfo objects. + * @see AttributeInfo + */ + public List getAttributes() { + return attributes; + } + + /** + * Returns the attribute with the specified name. If there are multiple + * attributes with that name, this method returns either of them. It + * returns null if the specified attributed is not found. + * + * @param name attribute name + * @see #getAttributes() + */ + public AttributeInfo getAttribute(String name) { + ArrayList list = attributes; + int n = list.size(); + for (int i = 0; i < n; ++i) { + AttributeInfo ai = (AttributeInfo)list.get(i); + if (ai.getName().equals(name)) + return ai; + } + + return null; + } + + /** + * Appends an attribute. If there is already an attribute with the same + * name, the new one substitutes for it. + * + * @see #getAttributes() + */ + public void addAttribute(AttributeInfo info) { + AttributeInfo.remove(attributes, info.getName()); + attributes.add(info); + } + + /** + * Returns the source file containing this class. + * + * @return null if this information is not available. + */ + public String getSourceFile() { + SourceFileAttribute sf + = (SourceFileAttribute)getAttribute(SourceFileAttribute.tag); + if (sf == null) + return null; + else + return sf.getFileName(); + } + + private void read(DataInputStream in) throws IOException { + int i, n; + int magic = in.readInt(); + if (magic != 0xCAFEBABE) + throw new IOException("bad magic number: " + Integer.toHexString(magic)); + + minor = in.readUnsignedShort(); + major = in.readUnsignedShort(); + constPool = new ConstPool(in); + accessFlags = in.readUnsignedShort(); + thisClass = in.readUnsignedShort(); + constPool.setThisClassInfo(thisClass); + superClass = in.readUnsignedShort(); + n = in.readUnsignedShort(); + if (n == 0) + interfaces = null; + else { + interfaces = new int[n]; + for (i = 0; i < n; ++i) + interfaces[i] = in.readUnsignedShort(); + } + + ConstPool cp = constPool; + n = in.readUnsignedShort(); + fields = new ArrayList(); + for (i = 0; i < n; ++i) + addField2(new FieldInfo(cp, in)); + + n = in.readUnsignedShort(); + methods = new ArrayList(); + for (i = 0; i < n; ++i) + addMethod2(new MethodInfo(cp, in)); + + attributes = new ArrayList(); + n = in.readUnsignedShort(); + for (i = 0; i < n; ++i) + addAttribute(AttributeInfo.read(cp, in)); + + thisclassname = constPool.getClassInfo(thisClass); + } + + /** + * Writes a class file represented by this object into an output stream. + */ + public void write(DataOutputStream out) throws IOException { + int i, n; + + out.writeInt(0xCAFEBABE); // magic + out.writeShort(minor); // minor version + out.writeShort(major); // major version + constPool.write(out); // constant pool + out.writeShort(accessFlags); + out.writeShort(thisClass); + out.writeShort(superClass); + + if (interfaces == null) + n = 0; + else + n = interfaces.length; + + out.writeShort(n); + for (i = 0; i < n; ++i) + out.writeShort(interfaces[i]); + + ArrayList list = fields; + n = list.size(); + out.writeShort(n); + for (i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + finfo.write(out); + } + + list = methods; + n = list.size(); + out.writeShort(n); + for (i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + minfo.write(out); + } + + out.writeShort(attributes.size()); + AttributeInfo.writeAll(attributes, out); + } + + /** + * Get the Major version. + * + * @return the major version + */ + public int getMajorVersion() { + return major; + } + + /** + * Set the major version. + * + * @param major + * the major version + */ + public void setMajorVersion(int major) { + this.major = major; + } + + /** + * Get the minor version. + * + * @return the minor version + */ + public int getMinorVersion() { + return minor; + } + + /** + * Set the minor version. + * + * @param minor + * the minor version + */ + public void setMinorVersion(int minor) { + this.minor = minor; + } + + /** + * Sets the major and minor version to Java 5. + * + * If the major version is older than 49, Java 5 + * extensions such as annotations are ignored + * by the JVM. + */ + public void setVersionToJava5() { + this.major = 49; + this.minor = 0; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java new file mode 100644 index 0000000..a7bd9c7 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java @@ -0,0 +1,153 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.PrintWriter; +import com.wenshuo.agent.javassist.Modifier; +import java.util.List; + +/** + * A utility class for priting the contents of a class file. + * It prints a constant pool table, fields, and methods in a + * human readable representation. + */ +public class ClassFilePrinter { + /** + * Prints the contents of a class file to the standard output stream. + */ + public static void print(ClassFile cf) { + print(cf, new PrintWriter(System.out, true)); + } + + /** + * Prints the contents of a class file. + */ + public static void print(ClassFile cf, PrintWriter out) { + List list; + int n; + + /* 0x0020 (SYNCHRONIZED) means ACC_SUPER if the modifiers + * are of a class. + */ + int mod + = AccessFlag.toModifier(cf.getAccessFlags() + & ~AccessFlag.SYNCHRONIZED); + out.println("major: " + cf.major + ", minor: " + cf.minor + + " modifiers: " + Integer.toHexString(cf.getAccessFlags())); + out.println(Modifier.toString(mod) + " class " + + cf.getName() + " extends " + cf.getSuperclass()); + + String[] infs = cf.getInterfaces(); + if (infs != null && infs.length > 0) { + out.print(" implements "); + out.print(infs[0]); + for (int i = 1; i < infs.length; ++i) + out.print(", " + infs[i]); + + out.println(); + } + + out.println(); + list = cf.getFields(); + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + int acc = finfo.getAccessFlags(); + out.println(Modifier.toString(AccessFlag.toModifier(acc)) + + " " + finfo.getName() + "\t" + + finfo.getDescriptor()); + printAttributes(finfo.getAttributes(), out, 'f'); + } + + out.println(); + list = cf.getMethods(); + n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + int acc = minfo.getAccessFlags(); + out.println(Modifier.toString(AccessFlag.toModifier(acc)) + + " " + minfo.getName() + "\t" + + minfo.getDescriptor()); + printAttributes(minfo.getAttributes(), out, 'm'); + out.println(); + } + + out.println(); + printAttributes(cf.getAttributes(), out, 'c'); + } + + static void printAttributes(List list, PrintWriter out, char kind) { + if (list == null) + return; + + int n = list.size(); + for (int i = 0; i < n; ++i) { + AttributeInfo ai = (AttributeInfo)list.get(i); + if (ai instanceof CodeAttribute) { + CodeAttribute ca = (CodeAttribute)ai; + out.println("attribute: " + ai.getName() + ": " + + ai.getClass().getName()); + out.println("max stack " + ca.getMaxStack() + + ", max locals " + ca.getMaxLocals() + + ", " + ca.getExceptionTable().size() + + " catch blocks"); + out.println(""); + printAttributes(ca.getAttributes(), out, kind); + out.println(""); + } + else if (ai instanceof AnnotationsAttribute) { + out.println("annnotation: " + ai.toString()); + } + else if (ai instanceof ParameterAnnotationsAttribute) { + out.println("parameter annnotations: " + ai.toString()); + } + else if (ai instanceof StackMapTable) { + out.println(""); + StackMapTable.Printer.print((StackMapTable)ai, out); + out.println(""); + } + else if (ai instanceof StackMap) { + out.println(""); + ((StackMap)ai).print(out); + out.println(""); + } + else if (ai instanceof SignatureAttribute) { + SignatureAttribute sa = (SignatureAttribute)ai; + String sig = sa.getSignature(); + out.println("signature: " + sig); + try { + String s; + if (kind == 'c') + s = SignatureAttribute.toClassSignature(sig).toString(); + else if (kind == 'm') + s = SignatureAttribute.toMethodSignature(sig).toString(); + else + s = SignatureAttribute.toFieldSignature(sig).toString(); + + out.println(" " + s); + } + catch (BadBytecode e) { + out.println(" syntax error"); + } + } + else + out.println("attribute: " + ai.getName() + + " (" + ai.get().length + " byte): " + + ai.getClass().getName()); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java new file mode 100644 index 0000000..39ab88b --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java @@ -0,0 +1,791 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.OutputStream; +import java.io.DataOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * A quick class-file writer. This is useful when a generated + * class file is simple and the code generation should be fast. + * + *

Example: + * + *

+ * ClassFileWriter cfw = new ClassFileWriter(ClassFile.JAVA_4, 0);
+ * ConstPoolWriter cpw = cfw.getConstPool();
+ *
+ * FieldWriter fw = cfw.getFieldWriter();
+ * fw.add(AccessFlag.PUBLIC, "value", "I", null);
+ * fw.add(AccessFlag.PUBLIC, "value2", "J", null);
+ *
+ * int thisClass = cpw.addClassInfo("sample/Test");
+ * int superClass = cpw.addClassInfo("java/lang/Object");
+ *
+ * MethodWriter mw = cfw.getMethodWriter();
+ *
+ * mw.begin(AccessFlag.PUBLIC, MethodInfo.nameInit, "()V", null, null);
+ * mw.add(Opcode.ALOAD_0);
+ * mw.add(Opcode.INVOKESPECIAL);
+ * int signature = cpw.addNameAndTypeInfo(MethodInfo.nameInit, "()V");
+ * mw.add16(cpw.addMethodrefInfo(superClass, signature));
+ * mw.add(Opcode.RETURN);
+ * mw.codeEnd(1, 1);
+ * mw.end(null, null);
+ *
+ * mw.begin(AccessFlag.PUBLIC, "one", "()I", null, null);
+ * mw.add(Opcode.ICONST_1);
+ * mw.add(Opcode.IRETURN);
+ * mw.codeEnd(1, 1);
+ * mw.end(null, null);
+ *
+ * byte[] classfile = cfw.end(AccessFlag.PUBLIC, thisClass, superClass,
+ *                            null, null);
+ * 
+ * + *

The code above generates the following class: + * + *

+ * package sample;
+ * public class Test {
+ *     public int value;
+ *     public long value2;
+ *     public Test() { super(); }
+ *     public one() { return 1; }
+ * }
+ * 
+ * + * @since 3.13 + */ +public class ClassFileWriter { + private ByteStream output; + private ConstPoolWriter constPool; + private FieldWriter fields; + private MethodWriter methods; + int thisClass, superClass; + + /** + * Constructs a class file writer. + * + * @param major the major version ({@link ClassFile#JAVA_4}, {@link ClassFile#JAVA_5}, ...). + * @param minor the minor version (0 for JDK 1.3 and later). + */ + public ClassFileWriter(int major, int minor) { + output = new ByteStream(512); + output.writeInt(0xCAFEBABE); // magic + output.writeShort(minor); + output.writeShort(major); + constPool = new ConstPoolWriter(output); + fields = new FieldWriter(constPool); + methods = new MethodWriter(constPool); + + } + + /** + * Returns a constant pool. + */ + public ConstPoolWriter getConstPool() { return constPool; } + + /** + * Returns a filed writer. + */ + public FieldWriter getFieldWriter() { return fields; } + + /** + * Returns a method writer. + */ + public MethodWriter getMethodWriter() { return methods; } + + /** + * Ends writing and returns the contents of the class file. + * + * @param accessFlags access flags. + * @param thisClass this class. an index indicating its CONSTANT_Class_info. + * @param superClass super class. an index indicating its CONSTANT_Class_info. + * @param interfaces implemented interfaces. + * index numbers indicating their ClassInfo. + * It may be null. + * @param aw attributes of the class file. May be null. + * + * @see AccessFlag + */ + public byte[] end(int accessFlags, int thisClass, int superClass, + int[] interfaces, AttributeWriter aw) { + constPool.end(); + output.writeShort(accessFlags); + output.writeShort(thisClass); + output.writeShort(superClass); + if (interfaces == null) + output.writeShort(0); + else { + int n = interfaces.length; + output.writeShort(n); + for (int i = 0; i < n; i++) + output.writeShort(interfaces[i]); + } + + output.enlarge(fields.dataSize() + methods.dataSize() + 6); + try { + output.writeShort(fields.size()); + fields.write(output); + + output.writeShort(methods.numOfMethods()); + methods.write(output); + } + catch (IOException e) {} + + writeAttribute(output, aw, 0); + return output.toByteArray(); + } + + /** + * Ends writing and writes the contents of the class file into the + * given output stream. + * + * @param accessFlags access flags. + * @param thisClass this class. an index indicating its CONSTANT_Class_info. + * @param superClass super class. an index indicating its CONSTANT_Class_info. + * @param interfaces implemented interfaces. + * index numbers indicating their CONSTATNT_Class_info. + * It may be null. + * @param aw attributes of the class file. May be null. + * + * @see AccessFlag + */ + public void end(DataOutputStream out, + int accessFlags, int thisClass, int superClass, + int[] interfaces, AttributeWriter aw) + throws IOException + { + constPool.end(); + output.writeTo(out); + out.writeShort(accessFlags); + out.writeShort(thisClass); + out.writeShort(superClass); + if (interfaces == null) + out.writeShort(0); + else { + int n = interfaces.length; + out.writeShort(n); + for (int i = 0; i < n; i++) + out.writeShort(interfaces[i]); + } + + out.writeShort(fields.size()); + fields.write(out); + + out.writeShort(methods.numOfMethods()); + methods.write(out); + if (aw == null) + out.writeShort(0); + else { + out.writeShort(aw.size()); + aw.write(out); + } + } + + /** + * This writes attributes. + * + *

For example, the following object writes a synthetic attribute: + * + *

+     * ConstPoolWriter cpw = ...;
+     * final int tag = cpw.addUtf8Info("Synthetic");
+     * AttributeWriter aw = new AttributeWriter() {
+     *     public int size() {
+     *         return 1;
+     *     }
+     *     public void write(DataOutputStream out) throws java.io.IOException {
+     *         out.writeShort(tag);
+     *         out.writeInt(0);
+     *     }
+     * };
+     * 
+ */ + public static interface AttributeWriter { + /** + * Returns the number of attributes that this writer will + * write. + */ + public int size(); + + /** + * Writes all the contents of the attributes. The binary representation + * of the contents is an array of attribute_info. + */ + public void write(DataOutputStream out) throws IOException; + } + + static void writeAttribute(ByteStream bs, AttributeWriter aw, int attrCount) { + if (aw == null) { + bs.writeShort(attrCount); + return; + } + + bs.writeShort(aw.size() + attrCount); + DataOutputStream dos = new DataOutputStream(bs); + try { + aw.write(dos); + dos.flush(); + } + catch (IOException e) {} + } + + /** + * Field. + */ + public static final class FieldWriter { + protected ByteStream output; + protected ConstPoolWriter constPool; + private int fieldCount; + + FieldWriter(ConstPoolWriter cp) { + output = new ByteStream(128); + constPool = cp; + fieldCount = 0; + } + + /** + * Adds a new field. + * + * @param accessFlags access flags. + * @param name the field name. + * @param descriptor the field type. + * @param aw the attributes of the field. may be null. + * @see AccessFlag + */ + public void add(int accessFlags, String name, String descriptor, AttributeWriter aw) { + int nameIndex = constPool.addUtf8Info(name); + int descIndex = constPool.addUtf8Info(descriptor); + add(accessFlags, nameIndex, descIndex, aw); + } + + /** + * Adds a new field. + * + * @param accessFlags access flags. + * @param name the field name. an index indicating its CONSTANT_Utf8_info. + * @param descriptor the field type. an index indicating its CONSTANT_Utf8_info. + * @param aw the attributes of the field. may be null. + * @see AccessFlag + */ + public void add(int accessFlags, int name, int descriptor, AttributeWriter aw) { + ++fieldCount; + output.writeShort(accessFlags); + output.writeShort(name); + output.writeShort(descriptor); + writeAttribute(output, aw, 0); + } + + int size() { return fieldCount; } + + int dataSize() { return output.size(); } + + /** + * Writes the added fields. + */ + void write(OutputStream out) throws IOException { + output.writeTo(out); + } + } + + /** + * Method. + */ + public static final class MethodWriter { + protected ByteStream output; + protected ConstPoolWriter constPool; + private int methodCount; + protected int codeIndex; + protected int throwsIndex; + protected int stackIndex; + + private int startPos; + private boolean isAbstract; + private int catchPos; + private int catchCount; + + MethodWriter(ConstPoolWriter cp) { + output = new ByteStream(256); + constPool = cp; + methodCount = 0; + codeIndex = 0; + throwsIndex = 0; + stackIndex = 0; + } + + /** + * Starts Adding a new method. + * + * @param accessFlags access flags. + * @param name the method name. + * @param descriptor the method signature. + * @param exceptions throws clause. It may be null. + * The class names must be the JVM-internal + * representations like java/lang/Exception. + * @param aw attributes to the Method_info. + */ + public void begin(int accessFlags, String name, String descriptor, + String[] exceptions, AttributeWriter aw) { + int nameIndex = constPool.addUtf8Info(name); + int descIndex = constPool.addUtf8Info(descriptor); + int[] intfs; + if (exceptions == null) + intfs = null; + else + intfs = constPool.addClassInfo(exceptions); + + begin(accessFlags, nameIndex, descIndex, intfs, aw); + } + + /** + * Starts adding a new method. + * + * @param accessFlags access flags. + * @param name the method name. an index indicating its CONSTANT_Utf8_info. + * @param descriptor the field type. an index indicating its CONSTANT_Utf8_info. + * @param exceptions throws clause. indexes indicating CONSTANT_Class_infos. + * It may be null. + * @param aw attributes to the Method_info. + */ + public void begin(int accessFlags, int name, int descriptor, int[] exceptions, AttributeWriter aw) { + ++methodCount; + output.writeShort(accessFlags); + output.writeShort(name); + output.writeShort(descriptor); + isAbstract = (accessFlags & AccessFlag.ABSTRACT) != 0; + + int attrCount = isAbstract ? 0 : 1; + if (exceptions != null) + ++attrCount; + + writeAttribute(output, aw, attrCount); + + if (exceptions != null) + writeThrows(exceptions); + + if (!isAbstract) { + if (codeIndex == 0) + codeIndex = constPool.addUtf8Info(CodeAttribute.tag); + + startPos = output.getPos(); + output.writeShort(codeIndex); + output.writeBlank(12); // attribute_length, maxStack, maxLocals, code_lenth + } + + catchPos = -1; + catchCount = 0; + } + + private void writeThrows(int[] exceptions) { + if (throwsIndex == 0) + throwsIndex = constPool.addUtf8Info(ExceptionsAttribute.tag); + + output.writeShort(throwsIndex); + output.writeInt(exceptions.length * 2 + 2); + output.writeShort(exceptions.length); + for (int i = 0; i < exceptions.length; i++) + output.writeShort(exceptions[i]); + } + + /** + * Appends an 8bit value of bytecode. + * + * @see Opcode + */ + public void add(int b) { + output.write(b); + } + + /** + * Appends a 16bit value of bytecode. + */ + public void add16(int b) { + output.writeShort(b); + } + + /** + * Appends a 32bit value of bytecode. + */ + public void add32(int b) { + output.writeInt(b); + } + + /** + * Appends a invokevirtual, inovkespecial, or invokestatic bytecode. + * + * @see Opcode + */ + public void addInvoke(int opcode, String targetClass, String methodName, + String descriptor) { + int target = constPool.addClassInfo(targetClass); + int nt = constPool.addNameAndTypeInfo(methodName, descriptor); + int method = constPool.addMethodrefInfo(target, nt); + add(opcode); + add16(method); + } + + /** + * Ends appending bytecode. + */ + public void codeEnd(int maxStack, int maxLocals) { + if (!isAbstract) { + output.writeShort(startPos + 6, maxStack); + output.writeShort(startPos + 8, maxLocals); + output.writeInt(startPos + 10, output.getPos() - startPos - 14); // code_length + catchPos = output.getPos(); + catchCount = 0; + output.writeShort(0); // number of catch clauses + } + } + + /** + * Appends an exception_table entry to the + * Code_attribute. This method is available + * only after the codeEnd method is called. + * + * @param catchType an index indicating a CONSTANT_Class_info. + */ + public void addCatch(int startPc, int endPc, int handlerPc, int catchType) { + ++catchCount; + output.writeShort(startPc); + output.writeShort(endPc); + output.writeShort(handlerPc); + output.writeShort(catchType); + } + + /** + * Ends adding a new method. The add method must be + * called before the end method is called. + * + * @param smap a stack map table. may be null. + * @param aw attributes to the Code_attribute. + * may be null. + */ + public void end(StackMapTable.Writer smap, AttributeWriter aw) { + if (isAbstract) + return; + + // exception_table_length + output.writeShort(catchPos, catchCount); + + int attrCount = smap == null ? 0 : 1; + writeAttribute(output, aw, attrCount); + + if (smap != null) { + if (stackIndex == 0) + stackIndex = constPool.addUtf8Info(StackMapTable.tag); + + output.writeShort(stackIndex); + byte[] data = smap.toByteArray(); + output.writeInt(data.length); + output.write(data); + } + + // Code attribute_length + output.writeInt(startPos + 2, output.getPos() - startPos - 6); + } + + /** + * Returns the length of the bytecode that has been added so far. + * + * @return the length in bytes. + * @since 3.19 + */ + public int size() { return output.getPos() - startPos - 14; } + + int numOfMethods() { return methodCount; } + + int dataSize() { return output.size(); } + + /** + * Writes the added methods. + */ + void write(OutputStream out) throws IOException { + output.writeTo(out); + } + } + + /** + * Constant Pool. + */ + public static final class ConstPoolWriter { + ByteStream output; + protected int startPos; + protected int num; + + ConstPoolWriter(ByteStream out) { + output = out; + startPos = out.getPos(); + num = 1; + output.writeShort(1); // number of entries + } + + /** + * Makes CONSTANT_Class_info objects for each class name. + * + * @return an array of indexes indicating CONSTANT_Class_infos. + */ + public int[] addClassInfo(String[] classNames) { + int n = classNames.length; + int[] result = new int[n]; + for (int i = 0; i < n; i++) + result[i] = addClassInfo(classNames[i]); + + return result; + } + + /** + * Adds a new CONSTANT_Class_info structure. + * + *

This also adds a CONSTANT_Utf8_info structure + * for storing the class name. + * + * @param jvmname the JVM-internal representation of a class name. + * e.g. java/lang/Object. + * @return the index of the added entry. + */ + public int addClassInfo(String jvmname) { + int utf8 = addUtf8Info(jvmname); + output.write(ClassInfo.tag); + output.writeShort(utf8); + return num++; + } + + /** + * Adds a new CONSTANT_Class_info structure. + * + * @param name name_index + * @return the index of the added entry. + */ + public int addClassInfo(int name) { + output.write(ClassInfo.tag); + output.writeShort(name); + return num++; + } + + /** + * Adds a new CONSTANT_NameAndType_info structure. + * + * @param name name_index + * @param type descriptor_index + * @return the index of the added entry. + */ + public int addNameAndTypeInfo(String name, String type) { + return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type)); + } + + /** + * Adds a new CONSTANT_NameAndType_info structure. + * + * @param name name_index + * @param type descriptor_index + * @return the index of the added entry. + */ + public int addNameAndTypeInfo(int name, int type) { + output.write(NameAndTypeInfo.tag); + output.writeShort(name); + output.writeShort(type); + return num++; + } + + /** + * Adds a new CONSTANT_Fieldref_info structure. + * + * @param classInfo class_index + * @param nameAndTypeInfo name_and_type_index. + * @return the index of the added entry. + */ + public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) { + output.write(FieldrefInfo.tag); + output.writeShort(classInfo); + output.writeShort(nameAndTypeInfo); + return num++; + } + + /** + * Adds a new CONSTANT_Methodref_info structure. + * + * @param classInfo class_index + * @param nameAndTypeInfo name_and_type_index. + * @return the index of the added entry. + */ + public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) { + output.write(MethodrefInfo.tag); + output.writeShort(classInfo); + output.writeShort(nameAndTypeInfo); + return num++; + } + + /** + * Adds a new CONSTANT_InterfaceMethodref_info + * structure. + * + * @param classInfo class_index + * @param nameAndTypeInfo name_and_type_index. + * @return the index of the added entry. + */ + public int addInterfaceMethodrefInfo(int classInfo, + int nameAndTypeInfo) { + output.write(InterfaceMethodrefInfo.tag); + output.writeShort(classInfo); + output.writeShort(nameAndTypeInfo); + return num++; + } + + /** + * Adds a new CONSTANT_MethodHandle_info + * structure. + * + * @param kind reference_kind + * such as {@link ConstPool#REF_invokeStatic REF_invokeStatic}. + * @param index reference_index. + * @return the index of the added entry. + * + * @since 3.17.1 + */ + public int addMethodHandleInfo(int kind, int index) { + output.write(MethodHandleInfo.tag); + output.write(kind); + output.writeShort(index); + return num++; + } + + /** + * Adds a new CONSTANT_MethodType_info + * structure. + * + * @param desc descriptor_index. + * @return the index of the added entry. + * + * @since 3.17.1 + */ + public int addMethodTypeInfo(int desc) { + output.write(MethodTypeInfo.tag); + output.writeShort(desc); + return num++; + } + + /** + * Adds a new CONSTANT_InvokeDynamic_info + * structure. + * + * @param bootstrap bootstrap_method_attr_index. + * @param nameAndTypeInfo name_and_type_index. + * @return the index of the added entry. + * + * @since 3.17.1 + */ + public int addInvokeDynamicInfo(int bootstrap, + int nameAndTypeInfo) { + output.write(InvokeDynamicInfo.tag); + output.writeShort(bootstrap); + output.writeShort(nameAndTypeInfo); + return num++; + } + + /** + * Adds a new CONSTANT_String_info + * structure. + * + *

This also adds a new CONSTANT_Utf8_info + * structure. + * + * @return the index of the added entry. + */ + public int addStringInfo(String str) { + int utf8 = addUtf8Info(str); + output.write(StringInfo.tag); + output.writeShort(utf8); + return num++; + } + + /** + * Adds a new CONSTANT_Integer_info + * structure. + * + * @return the index of the added entry. + */ + public int addIntegerInfo(int i) { + output.write(IntegerInfo.tag); + output.writeInt(i); + return num++; + } + + /** + * Adds a new CONSTANT_Float_info + * structure. + * + * @return the index of the added entry. + */ + public int addFloatInfo(float f) { + output.write(FloatInfo.tag); + output.writeFloat(f); + return num++; + } + + /** + * Adds a new CONSTANT_Long_info + * structure. + * + * @return the index of the added entry. + */ + public int addLongInfo(long l) { + output.write(LongInfo.tag); + output.writeLong(l); + int n = num; + num += 2; + return n; + } + + /** + * Adds a new CONSTANT_Double_info + * structure. + * + * @return the index of the added entry. + */ + public int addDoubleInfo(double d) { + output.write(DoubleInfo.tag); + output.writeDouble(d); + int n = num; + num += 2; + return n; + } + + /** + * Adds a new CONSTANT_Utf8_info + * structure. + * + * @return the index of the added entry. + */ + public int addUtf8Info(String utf8) { + output.write(Utf8Info.tag); + output.writeUTF(utf8); + return num++; + } + + /** + * Writes the contents of this class pool. + */ + void end() { + output.writeShort(startPos, num); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java new file mode 100644 index 0000000..8efc6d4 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java @@ -0,0 +1,267 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +/** + * Utility for computing max_stack. + */ +class CodeAnalyzer implements Opcode { + private ConstPool constPool; + private CodeAttribute codeAttr; + + public CodeAnalyzer(CodeAttribute ca) { + codeAttr = ca; + constPool = ca.getConstPool(); + } + + public int computeMaxStack() + throws BadBytecode + { + /* d = stack[i] + * d == 0: not visited + * d > 0: the depth is d - 1 after executing the bytecode at i. + * d < 0: not visited. the initial depth (before execution) is 1 - d. + */ + CodeIterator ci = codeAttr.iterator(); + int length = ci.getCodeLength(); + int[] stack = new int[length]; + constPool = codeAttr.getConstPool(); + initStack(stack, codeAttr); + boolean repeat; + do { + repeat = false; + for (int i = 0; i < length; ++i) + if (stack[i] < 0) { + repeat = true; + visitBytecode(ci, stack, i); + } + } while (repeat); + + int maxStack = 1; + for (int i = 0; i < length; ++i) + if (stack[i] > maxStack) + maxStack = stack[i]; + + return maxStack - 1; // the base is 1. + } + + private void initStack(int[] stack, CodeAttribute ca) { + stack[0] = -1; + ExceptionTable et = ca.getExceptionTable(); + if (et != null) { + int size = et.size(); + for (int i = 0; i < size; ++i) + stack[et.handlerPc(i)] = -2; // an exception is on stack + } + } + + private void visitBytecode(CodeIterator ci, int[] stack, int index) + throws BadBytecode + { + int codeLength = stack.length; + ci.move(index); + int stackDepth = -stack[index]; + int[] jsrDepth = new int[1]; + jsrDepth[0] = -1; + while (ci.hasNext()) { + index = ci.next(); + stack[index] = stackDepth; + int op = ci.byteAt(index); + stackDepth = visitInst(op, ci, index, stackDepth); + if (stackDepth < 1) + throw new BadBytecode("stack underflow at " + index); + + if (processBranch(op, ci, index, codeLength, stack, stackDepth, jsrDepth)) + break; + + if (isEnd(op)) // return, ireturn, athrow, ... + break; + + if (op == JSR || op == JSR_W) + --stackDepth; + } + } + + private boolean processBranch(int opcode, CodeIterator ci, int index, + int codeLength, int[] stack, int stackDepth, int[] jsrDepth) + throws BadBytecode + { + if ((IFEQ <= opcode && opcode <= IF_ACMPNE) + || opcode == IFNULL || opcode == IFNONNULL) { + int target = index + ci.s16bitAt(index + 1); + checkTarget(index, target, codeLength, stack, stackDepth); + } + else { + int target, index2; + switch (opcode) { + case GOTO : + target = index + ci.s16bitAt(index + 1); + checkTarget(index, target, codeLength, stack, stackDepth); + return true; + case GOTO_W : + target = index + ci.s32bitAt(index + 1); + checkTarget(index, target, codeLength, stack, stackDepth); + return true; + case JSR : + case JSR_W : + if (opcode == JSR) + target = index + ci.s16bitAt(index + 1); + else + target = index + ci.s32bitAt(index + 1); + + checkTarget(index, target, codeLength, stack, stackDepth); + /* + * It is unknown which RET comes back to this JSR. + * So we assume that if the stack depth at one JSR instruction + * is N, then it is also N at other JSRs and N - 1 at all RET + * instructions. Note that STACK_GROW[JSR] is 1 since it pushes + * a return address on the operand stack. + */ + if (jsrDepth[0] < 0) { + jsrDepth[0] = stackDepth; + return false; + } + else if (stackDepth == jsrDepth[0]) + return false; + else + throw new BadBytecode( + "sorry, cannot compute this data flow due to JSR: " + + stackDepth + "," + jsrDepth[0]); + case RET : + if (jsrDepth[0] < 0) { + jsrDepth[0] = stackDepth + 1; + return false; + } + else if (stackDepth + 1 == jsrDepth[0]) + return true; + else + throw new BadBytecode( + "sorry, cannot compute this data flow due to RET: " + + stackDepth + "," + jsrDepth[0]); + case LOOKUPSWITCH : + case TABLESWITCH : + index2 = (index & ~3) + 4; + target = index + ci.s32bitAt(index2); + checkTarget(index, target, codeLength, stack, stackDepth); + if (opcode == LOOKUPSWITCH) { + int npairs = ci.s32bitAt(index2 + 4); + index2 += 12; + for (int i = 0; i < npairs; ++i) { + target = index + ci.s32bitAt(index2); + checkTarget(index, target, codeLength, + stack, stackDepth); + index2 += 8; + } + } + else { + int low = ci.s32bitAt(index2 + 4); + int high = ci.s32bitAt(index2 + 8); + int n = high - low + 1; + index2 += 12; + for (int i = 0; i < n; ++i) { + target = index + ci.s32bitAt(index2); + checkTarget(index, target, codeLength, + stack, stackDepth); + index2 += 4; + } + } + + return true; // always branch. + } + } + + return false; // may not branch. + } + + private void checkTarget(int opIndex, int target, int codeLength, + int[] stack, int stackDepth) + throws BadBytecode + { + if (target < 0 || codeLength <= target) + throw new BadBytecode("bad branch offset at " + opIndex); + + int d = stack[target]; + if (d == 0) + stack[target] = -stackDepth; + else if (d != stackDepth && d != -stackDepth) + throw new BadBytecode("verification error (" + stackDepth + + "," + d + ") at " + opIndex); + } + + private static boolean isEnd(int opcode) { + return (IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW; + } + + /** + * Visits an instruction. + */ + private int visitInst(int op, CodeIterator ci, int index, int stack) + throws BadBytecode + { + String desc; + switch (op) { + case GETFIELD : + stack += getFieldSize(ci, index) - 1; + break; + case PUTFIELD : + stack -= getFieldSize(ci, index) + 1; + break; + case GETSTATIC : + stack += getFieldSize(ci, index); + break; + case PUTSTATIC : + stack -= getFieldSize(ci, index); + break; + case INVOKEVIRTUAL : + case INVOKESPECIAL : + desc = constPool.getMethodrefType(ci.u16bitAt(index + 1)); + stack += Descriptor.dataSize(desc) - 1; + break; + case INVOKESTATIC : + desc = constPool.getMethodrefType(ci.u16bitAt(index + 1)); + stack += Descriptor.dataSize(desc); + break; + case INVOKEINTERFACE : + desc = constPool.getInterfaceMethodrefType( + ci.u16bitAt(index + 1)); + stack += Descriptor.dataSize(desc) - 1; + break; + case INVOKEDYNAMIC : + desc = constPool.getInvokeDynamicType(ci.u16bitAt(index + 1)); + stack += Descriptor.dataSize(desc); // assume CosntPool#REF_invokeStatic + break; + case ATHROW : + stack = 1; // the stack becomes empty (1 means no values). + break; + case MULTIANEWARRAY : + stack += 1 - ci.byteAt(index + 3); + break; + case WIDE : + op = ci.byteAt(index + 1); + // don't break here. + default : + stack += STACK_GROW[op]; + } + + return stack; + } + + private int getFieldSize(CodeIterator ci, int index) { + String desc = constPool.getFieldrefType(ci.u16bitAt(index + 1)); + return Descriptor.dataSize(desc); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java new file mode 100644 index 0000000..86e7bd9 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java @@ -0,0 +1,595 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +/** + * Code_attribute. + * + *

To browse the code field of + * a Code_attribute structure, + * use CodeIterator. + * + * @see CodeIterator + * @see #iterator() + */ +public class CodeAttribute extends AttributeInfo implements Opcode { + /** + * The name of this attribute "Code". + */ + public static final String tag = "Code"; + + // code[] is stored in AttributeInfo.info. + + private int maxStack; + private int maxLocals; + private ExceptionTable exceptions; + private ArrayList attributes; + + /** + * Constructs a Code_attribute. + * + * @param cp constant pool table + * @param stack max_stack + * @param locals max_locals + * @param code code[] + * @param etable exception_table[] + */ + public CodeAttribute(ConstPool cp, int stack, int locals, byte[] code, + ExceptionTable etable) + { + super(cp, tag); + maxStack = stack; + maxLocals = locals; + info = code; + exceptions = etable; + attributes = new ArrayList(); + } + + /** + * Constructs a copy of Code_attribute. + * Specified class names are replaced during the copy. + * + * @param cp constant pool table. + * @param src source Code attribute. + * @param classnames pairs of replaced and substituted + * class names. + */ + private CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames) + throws BadBytecode + { + super(cp, tag); + + maxStack = src.getMaxStack(); + maxLocals = src.getMaxLocals(); + exceptions = src.getExceptionTable().copy(cp, classnames); + attributes = new ArrayList(); + List src_attr = src.getAttributes(); + int num = src_attr.size(); + for (int i = 0; i < num; ++i) { + AttributeInfo ai = (AttributeInfo)src_attr.get(i); + attributes.add(ai.copy(cp, classnames)); + } + + info = src.copyCode(cp, classnames, exceptions, this); + } + + CodeAttribute(ConstPool cp, int name_id, DataInputStream in) + throws IOException + { + super(cp, name_id, (byte[])null); + int attr_len = in.readInt(); + + maxStack = in.readUnsignedShort(); + maxLocals = in.readUnsignedShort(); + + int code_len = in.readInt(); + info = new byte[code_len]; + in.readFully(info); + + exceptions = new ExceptionTable(cp, in); + + attributes = new ArrayList(); + int num = in.readUnsignedShort(); + for (int i = 0; i < num; ++i) + attributes.add(AttributeInfo.read(cp, in)); + } + + /** + * Makes a copy. Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. + * @exception RuntimeCopyException if a BadBytecode + * exception is thrown, it is + * converted into + * RuntimeCopyException. + * + * @return CodeAttribute object. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) + throws RuntimeCopyException + { + try { + return new CodeAttribute(newCp, this, classnames); + } + catch (BadBytecode e) { + throw new RuntimeCopyException("bad bytecode. fatal?"); + } + } + + /** + * An exception that may be thrown by copy() + * in CodeAttribute. + */ + public static class RuntimeCopyException extends RuntimeException { + /** + * Constructs an exception. + */ + public RuntimeCopyException(String s) { + super(s); + } + } + + /** + * Returns the length of this attribute_info + * structure. + * The returned value is attribute_length + 6. + */ + public int length() { + return 18 + info.length + exceptions.size() * 8 + + AttributeInfo.getLength(attributes); + } + + void write(DataOutputStream out) throws IOException { + out.writeShort(name); // attribute_name_index + out.writeInt(length() - 6); // attribute_length + out.writeShort(maxStack); // max_stack + out.writeShort(maxLocals); // max_locals + out.writeInt(info.length); // code_length + out.write(info); // code + exceptions.write(out); + out.writeShort(attributes.size()); // attributes_count + AttributeInfo.writeAll(attributes, out); // attributes + } + + /** + * This method is not available. + * + * @throws java.lang.UnsupportedOperationException always thrown. + */ + public byte[] get() { + throw new UnsupportedOperationException("CodeAttribute.get()"); + } + + /** + * This method is not available. + * + * @throws java.lang.UnsupportedOperationException always thrown. + */ + public void set(byte[] newinfo) { + throw new UnsupportedOperationException("CodeAttribute.set()"); + } + + void renameClass(String oldname, String newname) { + AttributeInfo.renameClass(attributes, oldname, newname); + } + + void renameClass(Map classnames) { + AttributeInfo.renameClass(attributes, classnames); + } + + void getRefClasses(Map classnames) { + AttributeInfo.getRefClasses(attributes, classnames); + } + + /** + * Returns the name of the class declaring the method including + * this code attribute. + */ + public String getDeclaringClass() { + ConstPool cp = getConstPool(); + return cp.getClassName(); + } + + /** + * Returns max_stack. + */ + public int getMaxStack() { + return maxStack; + } + + /** + * Sets max_stack. + */ + public void setMaxStack(int value) { + maxStack = value; + } + + /** + * Computes the maximum stack size and sets max_stack + * to the computed size. + * + * @throws BadBytecode if this method fails in computing. + * @return the newly computed value of max_stack + */ + public int computeMaxStack() throws BadBytecode { + maxStack = new CodeAnalyzer(this).computeMaxStack(); + return maxStack; + } + + /** + * Returns max_locals. + */ + public int getMaxLocals() { + return maxLocals; + } + + /** + * Sets max_locals. + */ + public void setMaxLocals(int value) { + maxLocals = value; + } + + /** + * Returns code_length. + */ + public int getCodeLength() { + return info.length; + } + + /** + * Returns code[]. + */ + public byte[] getCode() { + return info; + } + + /** + * Sets code[]. + */ + void setCode(byte[] newinfo) { super.set(newinfo); } + + /** + * Makes a new iterator for reading this code attribute. + */ + public CodeIterator iterator() { + return new CodeIterator(this); + } + + /** + * Returns exception_table[]. + */ + public ExceptionTable getExceptionTable() { return exceptions; } + + /** + * Returns attributes[]. + * It returns a list of AttributeInfo. + * A new element can be added to the returned list + * and an existing element can be removed from the list. + * + * @see AttributeInfo + */ + public List getAttributes() { return attributes; } + + /** + * Returns the attribute with the specified name. + * If it is not found, this method returns null. + * + * @param name attribute name + * @return an AttributeInfo object or null. + */ + public AttributeInfo getAttribute(String name) { + return AttributeInfo.lookup(attributes, name); + } + + /** + * Adds a stack map table. If another copy of stack map table + * is already contained, the old one is removed. + * + * @param smt the stack map table added to this code attribute. + * If it is null, a new stack map is not added. + * Only the old stack map is removed. + */ + public void setAttribute(StackMapTable smt) { + AttributeInfo.remove(attributes, StackMapTable.tag); + if (smt != null) + attributes.add(smt); + } + + /** + * Adds a stack map table for J2ME (CLDC). If another copy of stack map table + * is already contained, the old one is removed. + * + * @param sm the stack map table added to this code attribute. + * If it is null, a new stack map is not added. + * Only the old stack map is removed. + * @since 3.12 + */ + public void setAttribute(StackMap sm) { + AttributeInfo.remove(attributes, StackMap.tag); + if (sm != null) + attributes.add(sm); + } + + /** + * Copies code. + */ + private byte[] copyCode(ConstPool destCp, Map classnames, + ExceptionTable etable, CodeAttribute destCa) + throws BadBytecode + { + int len = getCodeLength(); + byte[] newCode = new byte[len]; + destCa.info = newCode; + LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(), + newCode, destCp, classnames); + return LdcEntry.doit(newCode, ldc, etable, destCa); + } + + private static LdcEntry copyCode(byte[] code, int beginPos, int endPos, + ConstPool srcCp, byte[] newcode, + ConstPool destCp, Map classnameMap) + throws BadBytecode + { + int i2, index; + LdcEntry ldcEntry = null; + + for (int i = beginPos; i < endPos; i = i2) { + i2 = CodeIterator.nextOpcode(code, i); + byte c = code[i]; + newcode[i] = c; + switch (c & 0xff) { + case LDC_W : + case LDC2_W : + case GETSTATIC : + case PUTSTATIC : + case GETFIELD : + case PUTFIELD : + case INVOKEVIRTUAL : + case INVOKESPECIAL : + case INVOKESTATIC : + case NEW : + case ANEWARRAY : + case CHECKCAST : + case INSTANCEOF : + copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, + classnameMap); + break; + case LDC : + index = code[i + 1] & 0xff; + index = srcCp.copy(index, destCp, classnameMap); + if (index < 0x100) + newcode[i + 1] = (byte)index; + else { + newcode[i] = NOP; + newcode[i + 1] = NOP; + LdcEntry ldc = new LdcEntry(); + ldc.where = i; + ldc.index = index; + ldc.next = ldcEntry; + ldcEntry = ldc; + } + break; + case INVOKEINTERFACE : + copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, + classnameMap); + newcode[i + 3] = code[i + 3]; + newcode[i + 4] = code[i + 4]; + break; + case INVOKEDYNAMIC : + copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, + classnameMap); + newcode[i + 3] = 0; + newcode[i + 4] = 0; + break; + case MULTIANEWARRAY : + copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, + classnameMap); + newcode[i + 3] = code[i + 3]; + break; + default : + while (++i < i2) + newcode[i] = code[i]; + + break; + } + } + + return ldcEntry; + } + + private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp, + byte[] newcode, ConstPool destCp, + Map classnameMap) { + int index = ((code[i] & 0xff) << 8) | (code[i + 1] & 0xff); + index = srcCp.copy(index, destCp, classnameMap); + newcode[i] = (byte)(index >> 8); + newcode[i + 1] = (byte)index; + } + + static class LdcEntry { + LdcEntry next; + int where; + int index; + + static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable, + CodeAttribute ca) + throws BadBytecode + { + if (ldc != null) + code = CodeIterator.changeLdcToLdcW(code, etable, ca, ldc); + + /* The original code was the following: + + while (ldc != null) { + int where = ldc.where; + code = CodeIterator.insertGapCore0(code, where, 1, false, etable, ca); + code[where] = (byte)Opcode.LDC_W; + ByteArray.write16bit(ldc.index, code, where + 1); + ldc = ldc.next; + } + + But this code does not support a large method > 32KB. + */ + + return code; + } + } + + /** + * Changes the index numbers of the local variables + * to append a new parameter. + * This method does not update LocalVariableAttribute, + * LocalVariableTypeAttribute, + * StackMapTable, or StackMap. + * These attributes must be explicitly updated. + * + * @param where the index of the new parameter. + * @param size the type size of the new parameter (1 or 2). + * + * @see LocalVariableAttribute#shiftIndex(int, int) + * @see LocalVariableTypeAttribute#shiftIndex(int, int) + * @see StackMapTable#insertLocal(int, int, int) + * @see StackMap#insertLocal(int, int, int) + */ + public void insertLocalVar(int where, int size) throws BadBytecode { + CodeIterator ci = iterator(); + while (ci.hasNext()) + shiftIndex(ci, where, size); + + setMaxLocals(getMaxLocals() + size); + } + + /** + * @param lessThan If the index of the local variable is + * less than this value, it does not change. + * Otherwise, the index is increased. + * @param delta the indexes of the local variables are + * increased by this value. + */ + private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode { + int index = ci.next(); + int opcode = ci.byteAt(index); + if (opcode < ILOAD) + return; + else if (opcode < IASTORE) { + if (opcode < ILOAD_0) { + // iload, lload, fload, dload, aload + shiftIndex8(ci, index, opcode, lessThan, delta); + } + else if (opcode < IALOAD) { + // iload_0, ..., aload_3 + shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD); + } + else if (opcode < ISTORE) + return; + else if (opcode < ISTORE_0) { + // istore, lstore, ... + shiftIndex8(ci, index, opcode, lessThan, delta); + } + else { + // istore_0, ..., astore_3 + shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE); + } + } + else if (opcode == IINC) { + int var = ci.byteAt(index + 1); + if (var < lessThan) + return; + + var += delta; + if (var < 0x100) + ci.writeByte(var, index + 1); + else { + int plus = (byte)ci.byteAt(index + 2); + int pos = ci.insertExGap(3); + ci.writeByte(WIDE, pos - 3); + ci.writeByte(IINC, pos - 2); + ci.write16bit(var, pos - 1); + ci.write16bit(plus, pos + 1); + } + } + else if (opcode == RET) + shiftIndex8(ci, index, opcode, lessThan, delta); + else if (opcode == WIDE) { + int var = ci.u16bitAt(index + 2); + if (var < lessThan) + return; + + var += delta; + ci.write16bit(var, index + 2); + } + } + + private static void shiftIndex8(CodeIterator ci, int index, int opcode, + int lessThan, int delta) + throws BadBytecode + { + int var = ci.byteAt(index + 1); + if (var < lessThan) + return; + + var += delta; + if (var < 0x100) + ci.writeByte(var, index + 1); + else { + int pos = ci.insertExGap(2); + ci.writeByte(WIDE, pos - 2); + ci.writeByte(opcode, pos - 1); + ci.write16bit(var, pos); + } + } + + private static void shiftIndex0(CodeIterator ci, int index, int opcode, + int lessThan, int delta, + int opcode_i_0, int opcode_i) + throws BadBytecode + { + int var = (opcode - opcode_i_0) % 4; + if (var < lessThan) + return; + + var += delta; + if (var < 4) + ci.writeByte(opcode + delta, index); + else { + opcode = (opcode - opcode_i_0) / 4 + opcode_i; + if (var < 0x100) { + int pos = ci.insertExGap(1); + ci.writeByte(opcode, pos - 1); + ci.writeByte(var, pos); + } + else { + int pos = ci.insertExGap(3); + ci.writeByte(WIDE, pos - 1); + ci.writeByte(opcode, pos); + ci.write16bit(var, pos + 1); + } + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeIterator.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeIterator.java new file mode 100644 index 0000000..7dfc316 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeIterator.java @@ -0,0 +1,1604 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.util.ArrayList; + +/** + * An iterator for editing a code attribute. + * + *

To directly read or edit a bytecode sequence, call {@link #byteAt(int)}, {@link #s16bitAt(int)}, + * {@link #writeByte(int, int)}, {@link #write16bit(int, int)}, and other methods. + * For example, if method refers to a CtMethod object, + * the following code substitutes the NOP instruction for the first + * instruction of the method: + * + *

+ * CodeAttribute ca = method.getMethodInfo().getCodeAttribute();
+ * CodeIterator ci = ca.iterator();
+ * ci.writeByte(Opcode.NOP, 0);
+ * + *

To visit every instruction, call {@link #next()} on a CodeIterator. + * It returns the index of the first byte of the next instruction. + * + *

If there are multiple CodeIterators referring to the + * same Code_attribute, then inserting a gap by one + * CodeIterator will break the other + * CodeIterator. + * + *

This iterator does not provide remove(). + * If a piece of code in a Code_attribute is unnecessary, + * it should be overwritten with NOP. + * + * @see CodeAttribute#iterator() + */ +public class CodeIterator implements Opcode { + protected CodeAttribute codeAttr; + protected byte[] bytecode; + protected int endPos; + protected int currentPos; + protected int mark; + + protected CodeIterator(CodeAttribute ca) { + codeAttr = ca; + bytecode = ca.getCode(); + begin(); + } + + /** + * Moves to the first instruction. + */ + public void begin() { + currentPos = mark = 0; + endPos = getCodeLength(); + } + + /** + * Moves to the given index. + * + *

The index of the next instruction is set to the given index. + * The successive call to next() + * returns the index that has been given to move(). + * + *

Note that the index is into the byte array returned by + * get().getCode(). + * + * @see CodeAttribute#getCode() + */ + public void move(int index) { + currentPos = index; + } + + /** + * Sets a mark to the bytecode at the given index. + * The mark can be used to track the position of that bytecode + * when code blocks are inserted. + * If a code block is inclusively inserted at the position of the + * bytecode, the mark is set to the inserted code block. + * + * @see #getMark() + * @since 3.11 + */ + public void setMark(int index) { + mark = index; + } + + /** + * Gets the index of the position of the mark set by + * setMark. + * + * @return the index of the position. + * @see #setMark(int) + * @since 3.11 + */ + public int getMark() { return mark; } + + /** + * Returns a Code attribute read with this iterator. + */ + public CodeAttribute get() { + return codeAttr; + } + + /** + * Returns code_length of Code_attribute. + */ + public int getCodeLength() { + return bytecode.length; + } + + /** + * Returns the unsigned 8bit value at the given index. + */ + public int byteAt(int index) { return bytecode[index] & 0xff; } + + /** + * Returns the signed 8bit value at the given index. + */ + public int signedByteAt(int index) { return bytecode[index]; } + + /** + * Writes an 8bit value at the given index. + */ + public void writeByte(int value, int index) { + bytecode[index] = (byte)value; + } + + /** + * Returns the unsigned 16bit value at the given index. + */ + public int u16bitAt(int index) { + return ByteArray.readU16bit(bytecode, index); + } + + /** + * Returns the signed 16bit value at the given index. + */ + public int s16bitAt(int index) { + return ByteArray.readS16bit(bytecode, index); + } + + /** + * Writes a 16 bit integer at the index. + */ + public void write16bit(int value, int index) { + ByteArray.write16bit(value, bytecode, index); + } + + /** + * Returns the signed 32bit value at the given index. + */ + public int s32bitAt(int index) { + return ByteArray.read32bit(bytecode, index); + } + + /** + * Writes a 32bit integer at the index. + */ + public void write32bit(int value, int index) { + ByteArray.write32bit(value, bytecode, index); + } + + /** + * Writes a byte array at the index. + * + * @param code may be a zero-length array. + */ + public void write(byte[] code, int index) { + int len = code.length; + for (int j = 0; j < len; ++j) + bytecode[index++] = code[j]; + } + + /** + * Returns true if there is more instructions. + */ + public boolean hasNext() { return currentPos < endPos; } + + /** + * Returns the index of the next instruction + * (not the operand following the current opcode). + * + *

Note that the index is into the byte array returned by + * get().getCode(). + * + * @see CodeAttribute#getCode() + * @see CodeIterator#byteAt(int) + */ + public int next() throws BadBytecode { + int pos = currentPos; + currentPos = nextOpcode(bytecode, pos); + return pos; + } + + /** + * Obtains the value that the next call + * to next() will return. + * + *

This method is side-effects free. + * Successive calls to lookAhead() return the + * same value until next() is called. + */ + public int lookAhead() { + return currentPos; + } + + /** + * Moves to the instruction for + * either super() or this(). + * + *

This method skips all the instructions for computing arguments + * to super() or this(), which should be + * placed at the beginning of a constructor body. + * + *

This method returns the index of INVOKESPECIAL instruction + * executing super() or this(). + * A successive call to next() returns the + * index of the next instruction following that INVOKESPECIAL. + * + *

This method works only for a constructor. + * + * @return the index of the INVOKESPECIAL instruction, or -1 + * if a constructor invocation is not found. + */ + public int skipConstructor() throws BadBytecode { + return skipSuperConstructor0(-1); + } + + /** + * Moves to the instruction for super(). + * + *

This method skips all the instructions for computing arguments to + * super(), which should be + * placed at the beginning of a constructor body. + * + *

This method returns the index of INVOKESPECIAL instruction + * executing super(). + * A successive call to next() returns the + * index of the next instruction following that INVOKESPECIAL. + * + *

This method works only for a constructor. + * + * @return the index of the INVOKESPECIAL instruction, or -1 + * if a super constructor invocation is not found + * but this() is found. + */ + public int skipSuperConstructor() throws BadBytecode { + return skipSuperConstructor0(0); + } + + /** + * Moves to the instruction for this(). + * + *

This method skips all the instructions for computing arguments to + * this(), which should be + * placed at the beginning of a constructor body. + * + *

This method returns the index of INVOKESPECIAL instruction + * executing this(). + * A successive call to next() returns the + * index of the next instruction following that INVOKESPECIAL. + * + *

This method works only for a constructor. + * + * @return the index of the INVOKESPECIAL instruction, or -1 + * if a explicit constructor invocation is not found + * but super() is found. + */ + public int skipThisConstructor() throws BadBytecode { + return skipSuperConstructor0(1); + } + + /* skipSuper 1: this(), 0: super(), -1: both. + */ + private int skipSuperConstructor0(int skipThis) throws BadBytecode { + begin(); + ConstPool cp = codeAttr.getConstPool(); + String thisClassName = codeAttr.getDeclaringClass(); + int nested = 0; + while (hasNext()) { + int index = next(); + int c = byteAt(index); + if (c == NEW) + ++nested; + else if (c == INVOKESPECIAL) { + int mref = ByteArray.readU16bit(bytecode, index + 1); + if (cp.getMethodrefName(mref).equals(MethodInfo.nameInit)) + if (--nested < 0) { + if (skipThis < 0) + return index; + + String cname = cp.getMethodrefClassName(mref); + if (cname.equals(thisClassName) == (skipThis > 0)) + return index; + else + break; + } + } + } + + begin(); + return -1; + } + + /** + * Inserts the given bytecode sequence + * before the next instruction that would be returned by + * next() (not before the instruction returned + * by the last call to next()). + * Branch offsets and the exception table are also updated. + * + *

If the next instruction is at the beginning of a block statement, + * then the bytecode is inserted within that block. + * + *

An extra gap may be inserted at the end of the inserted + * bytecode sequence for adjusting alignment if the code attribute + * includes LOOKUPSWITCH or TABLESWITCH. + * + * @param code inserted bytecode sequence. + * @return the index indicating the first byte of the + * inserted byte sequence. + */ + public int insert(byte[] code) + throws BadBytecode + { + return insert0(currentPos, code, false); + } + + /** + * Inserts the given bytecode sequence + * before the instruction at the given index pos. + * Branch offsets and the exception table are also updated. + * + *

If the instruction at the given index is at the beginning + * of a block statement, + * then the bytecode is inserted within that block. + * + *

An extra gap may be inserted at the end of the inserted + * bytecode sequence for adjusting alignment if the code attribute + * includes LOOKUPSWITCH or TABLESWITCH. + * + *

The index at which the byte sequence is actually inserted + * might be different from pos since some other bytes might be + * inserted at other positions (e.g. to change GOTO + * to GOTO_W). + * + * @param pos the index at which a byte sequence is inserted. + * @param code inserted bytecode sequence. + */ + public void insert(int pos, byte[] code) throws BadBytecode { + insert0(pos, code, false); + } + + /** + * Inserts the given bytecode sequence + * before the instruction at the given index pos. + * Branch offsets and the exception table are also updated. + * + *

If the instruction at the given index is at the beginning + * of a block statement, + * then the bytecode is inserted within that block. + * + *

An extra gap may be inserted at the end of the inserted + * bytecode sequence for adjusting alignment if the code attribute + * includes LOOKUPSWITCH or TABLESWITCH. + * + * @param pos the index at which a byte sequence is inserted. + * @param code inserted bytecode sequence. + * @return the index indicating the first byte of the + * inserted byte sequence, which might be + * different from pos. + * @since 3.11 + */ + public int insertAt(int pos, byte[] code) throws BadBytecode { + return insert0(pos, code, false); + } + + /** + * Inserts the given bytecode sequence exclusively + * before the next instruction that would be returned by + * next() (not before the instruction returned + * by tha last call to next()). + * Branch offsets and the exception table are also updated. + * + *

If the next instruction is at the beginning of a block statement, + * then the bytecode is excluded from that block. + * + *

An extra gap may be inserted at the end of the inserted + * bytecode sequence for adjusting alignment if the code attribute + * includes LOOKUPSWITCH or TABLESWITCH. + * + * @param code inserted bytecode sequence. + * @return the index indicating the first byte of the + * inserted byte sequence. + */ + public int insertEx(byte[] code) + throws BadBytecode + { + return insert0(currentPos, code, true); + } + + /** + * Inserts the given bytecode sequence exclusively + * before the instruction at the given index pos. + * Branch offsets and the exception table are also updated. + * + *

If the instruction at the given index is at the beginning + * of a block statement, + * then the bytecode is excluded from that block. + * + *

An extra gap may be inserted at the end of the inserted + * bytecode sequence for adjusting alignment if the code attribute + * includes LOOKUPSWITCH or TABLESWITCH. + * + *

The index at which the byte sequence is actually inserted + * might be different from pos since some other bytes might be + * inserted at other positions (e.g. to change GOTO + * to GOTO_W). + * + * @param pos the index at which a byte sequence is inserted. + * @param code inserted bytecode sequence. + */ + public void insertEx(int pos, byte[] code) throws BadBytecode { + insert0(pos, code, true); + } + + /** + * Inserts the given bytecode sequence exclusively + * before the instruction at the given index pos. + * Branch offsets and the exception table are also updated. + * + *

If the instruction at the given index is at the beginning + * of a block statement, + * then the bytecode is excluded from that block. + * + *

An extra gap may be inserted at the end of the inserted + * bytecode sequence for adjusting alignment if the code attribute + * includes LOOKUPSWITCH or TABLESWITCH. + * + * @param pos the index at which a byte sequence is inserted. + * @param code inserted bytecode sequence. + * @return the index indicating the first byte of the + * inserted byte sequence, which might be + * different from pos. + * @since 3.11 + */ + public int insertExAt(int pos, byte[] code) throws BadBytecode { + return insert0(pos, code, true); + } + + /** + * @return the index indicating the first byte of the + * inserted byte sequence. + */ + private int insert0(int pos, byte[] code, boolean exclusive) + throws BadBytecode + { + int len = code.length; + if (len <= 0) + return pos; + + // currentPos will change. + pos = insertGapAt(pos, len, exclusive).position; + + int p = pos; + for (int j = 0; j < len; ++j) + bytecode[p++] = code[j]; + + return pos; + } + + /** + * Inserts a gap + * before the next instruction that would be returned by + * next() (not before the instruction returned + * by the last call to next()). + * Branch offsets and the exception table are also updated. + * The inserted gap is filled with NOP. The gap length may be + * extended to a multiple of 4. + * + *

If the next instruction is at the beginning of a block statement, + * then the gap is inserted within that block. + * + * @param length gap length + * @return the index indicating the first byte of the inserted gap. + */ + public int insertGap(int length) throws BadBytecode { + return insertGapAt(currentPos, length, false).position; + } + + /** + * Inserts a gap in front of the instruction at the given + * index pos. + * Branch offsets and the exception table are also updated. + * The inserted gap is filled with NOP. The gap length may be + * extended to a multiple of 4. + * + *

If the instruction at the given index is at the beginning + * of a block statement, + * then the gap is inserted within that block. + * + * @param pos the index at which a gap is inserted. + * @param length gap length. + * @return the length of the inserted gap. + * It might be bigger than length. + */ + public int insertGap(int pos, int length) throws BadBytecode { + return insertGapAt(pos, length, false).length; + } + + /** + * Inserts an exclusive gap + * before the next instruction that would be returned by + * next() (not before the instruction returned + * by the last call to next()). + * Branch offsets and the exception table are also updated. + * The inserted gap is filled with NOP. The gap length may be + * extended to a multiple of 4. + * + *

If the next instruction is at the beginning of a block statement, + * then the gap is excluded from that block. + * + * @param length gap length + * @return the index indicating the first byte of the inserted gap. + */ + public int insertExGap(int length) throws BadBytecode { + return insertGapAt(currentPos, length, true).position; + } + + /** + * Inserts an exclusive gap in front of the instruction at the given + * index pos. + * Branch offsets and the exception table are also updated. + * The inserted gap is filled with NOP. The gap length may be + * extended to a multiple of 4. + * + *

If the instruction at the given index is at the beginning + * of a block statement, + * then the gap is excluded from that block. + * + * @param pos the index at which a gap is inserted. + * @param length gap length. + * @return the length of the inserted gap. + * It might be bigger than length. + */ + public int insertExGap(int pos, int length) throws BadBytecode { + return insertGapAt(pos, length, true).length; + } + + /** + * An inserted gap. + * + * @since 3.11 + */ + public static class Gap { + /** + * The position of the gap. + */ + public int position; + + /** + * The length of the gap. + */ + public int length; + } + + /** + * Inserts an inclusive or exclusive gap in front of the instruction + * at the given index pos. + * Branch offsets and the exception table in the method body + * are also updated. The inserted gap is filled with NOP. + * The gap length may be extended to a multiple of 4. + * + *

Suppose that the instruction at the given index is at the + * beginning of a block statement. If the gap is inclusive, + * then it is included within that block. If the gap is exclusive, + * then it is excluded from that block. + * + *

The index at which the gap is actually inserted + * might be different from pos since some other bytes might be + * inserted at other positions (e.g. to change GOTO + * to GOTO_W). The index is available from the Gap + * object returned by this method. + * + *

Suppose that the gap is inserted at the position of + * the next instruction that would be returned by + * next() (not the last instruction returned + * by the last call to next()). The next + * instruction returned by next() after the gap is + * inserted is still the same instruction. It is not NOP + * at the first byte of the inserted gap. + * + * @param pos the index at which a gap is inserted. + * @param length gap length. + * @param exclusive true if exclusive, otherwise false. + * @return the position and the length of the inserted gap. + * @since 3.11 + */ + public Gap insertGapAt(int pos, int length, boolean exclusive) + throws BadBytecode + { + /** + * cursorPos indicates the next bytecode whichever exclusive is + * true or false. + */ + Gap gap = new Gap(); + if (length <= 0) { + gap.position = pos; + gap.length = 0; + return gap; + } + + byte[] c; + int length2; + if (bytecode.length + length > Short.MAX_VALUE) { + // currentPos might change after calling insertGapCore0w(). + c = insertGapCore0w(bytecode, pos, length, exclusive, + get().getExceptionTable(), codeAttr, gap); + pos = gap.position; + length2 = length; // == gap.length + } + else { + int cur = currentPos; + c = insertGapCore0(bytecode, pos, length, exclusive, + get().getExceptionTable(), codeAttr); + // insertGapCore0() never changes pos. + length2 = c.length - bytecode.length; + gap.position = pos; + gap.length = length2; + if (cur >= pos) + currentPos = cur + length2; + + if (mark > pos || (mark == pos && exclusive)) + mark += length2; + } + + codeAttr.setCode(c); + bytecode = c; + endPos = getCodeLength(); + updateCursors(pos, length2); + return gap; + } + + /** + * Is called when a gap is inserted. The default implementation is empty. + * A subclass can override this method so that cursors will be updated. + * + * @param pos the position where a gap is inserted. + * @param length the length of the gap. + */ + protected void updateCursors(int pos, int length) { + // empty + } + + /** + * Copies and inserts the entries in the given exception table + * at the beginning of the exception table in the code attribute + * edited by this object. + * + * @param offset the value added to the code positions included + * in the entries. + */ + public void insert(ExceptionTable et, int offset) { + codeAttr.getExceptionTable().add(0, et, offset); + } + + /** + * Appends the given bytecode sequence at the end. + * + * @param code the bytecode appended. + * @return the position of the first byte of the appended bytecode. + */ + public int append(byte[] code) { + int size = getCodeLength(); + int len = code.length; + if (len <= 0) + return size; + + appendGap(len); + byte[] dest = bytecode; + for (int i = 0; i < len; ++i) + dest[i + size] = code[i]; + + return size; + } + + /** + * Appends a gap at the end of the bytecode sequence. + * + * @param gapLength gap length + */ + public void appendGap(int gapLength) { + byte[] code = bytecode; + int codeLength = code.length; + byte[] newcode = new byte[codeLength + gapLength]; + + int i; + for (i = 0; i < codeLength; ++i) + newcode[i] = code[i]; + + for (i = codeLength; i < codeLength + gapLength; ++i) + newcode[i] = NOP; + + codeAttr.setCode(newcode); + bytecode = newcode; + endPos = getCodeLength(); + } + + /** + * Copies and appends the entries in the given exception table + * at the end of the exception table in the code attribute + * edited by this object. + * + * @param offset the value added to the code positions included + * in the entries. + */ + public void append(ExceptionTable et, int offset) { + ExceptionTable table = codeAttr.getExceptionTable(); + table.add(table.size(), et, offset); + } + + /* opcodeLegth is used for implementing nextOpcode(). + */ + private static final int opcodeLength[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, + 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3, + 3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, + 5, 5 + }; + // 0 .. LOOKUPSWITCH, TABLESWITCH, WIDE + + /** + * Calculates the index of the next opcode. + */ + static int nextOpcode(byte[] code, int index) + throws BadBytecode + { + int opcode; + try { + opcode = code[index] & 0xff; + } + catch (IndexOutOfBoundsException e) { + throw new BadBytecode("invalid opcode address"); + } + + try { + int len = opcodeLength[opcode]; + if (len > 0) + return index + len; + else if (opcode == WIDE) + if (code[index + 1] == (byte)IINC) // WIDE IINC + return index + 6; + else + return index + 4; // WIDE ... + else { + int index2 = (index & ~3) + 8; + if (opcode == LOOKUPSWITCH) { + int npairs = ByteArray.read32bit(code, index2); + return index2 + npairs * 8 + 4; + } + else if (opcode == TABLESWITCH) { + int low = ByteArray.read32bit(code, index2); + int high = ByteArray.read32bit(code, index2 + 4); + return index2 + (high - low + 1) * 4 + 8; + } + // else + // throw new BadBytecode(opcode); + } + } + catch (IndexOutOfBoundsException e) { + } + + // opcode is UNUSED or an IndexOutOfBoundsException was thrown. + throw new BadBytecode(opcode); + } + + // methods for implementing insertGap(). + + static class AlignmentException extends Exception {} + + /** + * insertGapCore0() inserts a gap (some NOPs). + * It cannot handle a long code sequence more than 32K. All branch offsets must be + * signed 16bits. + * + * If "where" is the beginning of a block statement and exclusive is false, + * then the inserted gap is also included in the block statement. + * "where" must indicate the first byte of an opcode. + * The inserted gap is filled with NOP. gapLength may be extended to + * a multiple of 4. + * + * This method was also called from CodeAttribute.LdcEntry.doit(). + * + * @param where It must indicate the first byte of an opcode. + */ + static byte[] insertGapCore0(byte[] code, int where, int gapLength, + boolean exclusive, ExceptionTable etable, CodeAttribute ca) + throws BadBytecode + { + if (gapLength <= 0) + return code; + + try { + return insertGapCore1(code, where, gapLength, exclusive, etable, ca); + } + catch (AlignmentException e) { + try { + return insertGapCore1(code, where, (gapLength + 3) & ~3, + exclusive, etable, ca); + } + catch (AlignmentException e2) { + throw new RuntimeException("fatal error?"); + } + } + } + + private static byte[] insertGapCore1(byte[] code, int where, int gapLength, + boolean exclusive, ExceptionTable etable, + CodeAttribute ca) + throws BadBytecode, AlignmentException + { + int codeLength = code.length; + byte[] newcode = new byte[codeLength + gapLength]; + insertGap2(code, where, gapLength, codeLength, newcode, exclusive); + etable.shiftPc(where, gapLength, exclusive); + LineNumberAttribute na + = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); + if (na != null) + na.shiftPc(where, gapLength, exclusive); + + LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( + LocalVariableAttribute.tag); + if (va != null) + va.shiftPc(where, gapLength, exclusive); + + LocalVariableAttribute vta + = (LocalVariableAttribute)ca.getAttribute( + LocalVariableAttribute.typeTag); + if (vta != null) + vta.shiftPc(where, gapLength, exclusive); + + StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); + if (smt != null) + smt.shiftPc(where, gapLength, exclusive); + + StackMap sm = (StackMap)ca.getAttribute(StackMap.tag); + if (sm != null) + sm.shiftPc(where, gapLength, exclusive); + + return newcode; + } + + private static void insertGap2(byte[] code, int where, int gapLength, + int endPos, byte[] newcode, boolean exclusive) + throws BadBytecode, AlignmentException + { + int nextPos; + int i = 0; + int j = 0; + for (; i < endPos; i = nextPos) { + if (i == where) { + int j2 = j + gapLength; + while (j < j2) + newcode[j++] = NOP; + } + + nextPos = nextOpcode(code, i); + int inst = code[i] & 0xff; + // if, if_icmp, if_acmp, goto, jsr + if ((153 <= inst && inst <= 168) + || inst == IFNULL || inst == IFNONNULL) { + /* 2bytes *signed* offset */ + int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff); + offset = newOffset(i, offset, where, gapLength, exclusive); + newcode[j] = code[i]; + ByteArray.write16bit(offset, newcode, j + 1); + j += 3; + } + else if (inst == GOTO_W || inst == JSR_W) { + /* 4bytes offset */ + int offset = ByteArray.read32bit(code, i + 1); + offset = newOffset(i, offset, where, gapLength, exclusive); + newcode[j++] = code[i]; + ByteArray.write32bit(offset, newcode, j); + j += 4; + } + else if (inst == TABLESWITCH) { + if (i != j && (gapLength & 3) != 0) + throw new AlignmentException(); + + int i2 = (i & ~3) + 4; // 0-3 byte padding + // IBM JVM 1.4.2 cannot run the following code: + // int i0 = i; + // while (i0 < i2) + // newcode[j++] = code[i0++]; + // So extracting this code into an external method. + // see JIRA JASSIST-74. + j = copyGapBytes(newcode, j, code, i, i2); + + int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2), + where, gapLength, exclusive); + ByteArray.write32bit(defaultbyte, newcode, j); + int lowbyte = ByteArray.read32bit(code, i2 + 4); + ByteArray.write32bit(lowbyte, newcode, j + 4); + int highbyte = ByteArray.read32bit(code, i2 + 8); + ByteArray.write32bit(highbyte, newcode, j + 8); + j += 12; + int i0 = i2 + 12; + i2 = i0 + (highbyte - lowbyte + 1) * 4; + while (i0 < i2) { + int offset = newOffset(i, ByteArray.read32bit(code, i0), + where, gapLength, exclusive); + ByteArray.write32bit(offset, newcode, j); + j += 4; + i0 += 4; + } + } + else if (inst == LOOKUPSWITCH) { + if (i != j && (gapLength & 3) != 0) + throw new AlignmentException(); + + int i2 = (i & ~3) + 4; // 0-3 byte padding + + // IBM JVM 1.4.2 cannot run the following code: + // int i0 = i; + // while (i0 < i2) + // newcode[j++] = code[i0++]; + // So extracting this code into an external method. + // see JIRA JASSIST-74. + j = copyGapBytes(newcode, j, code, i, i2); + + int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2), + where, gapLength, exclusive); + ByteArray.write32bit(defaultbyte, newcode, j); + int npairs = ByteArray.read32bit(code, i2 + 4); + ByteArray.write32bit(npairs, newcode, j + 4); + j += 8; + int i0 = i2 + 8; + i2 = i0 + npairs * 8; + while (i0 < i2) { + ByteArray.copy32bit(code, i0, newcode, j); + int offset = newOffset(i, + ByteArray.read32bit(code, i0 + 4), + where, gapLength, exclusive); + ByteArray.write32bit(offset, newcode, j + 4); + j += 8; + i0 += 8; + } + } + else + while (i < nextPos) + newcode[j++] = code[i++]; + } + } + + + private static int copyGapBytes(byte[] newcode, int j, byte[] code, int i, int iEnd) { + switch (iEnd - i) { + case 4: + newcode[j++] = code[i++]; + case 3: + newcode[j++] = code[i++]; + case 2: + newcode[j++] = code[i++]; + case 1: + newcode[j++] = code[i++]; + default: + } + + return j; + } + + private static int newOffset(int i, int offset, int where, + int gapLength, boolean exclusive) { + int target = i + offset; + if (i < where) { + if (where < target || (exclusive && where == target)) + offset += gapLength; + } + else if (i == where) { + // This code is different from the code in Branch#shiftOffset(). + // see JASSIST-124. + if (target < where) + offset -= gapLength; + } + else + if (target < where || (!exclusive && where == target)) + offset -= gapLength; + + return offset; + } + + static class Pointers { + int cursor; + int mark0, mark; + ExceptionTable etable; + LineNumberAttribute line; + LocalVariableAttribute vars, types; + StackMapTable stack; + StackMap stack2; + + Pointers(int cur, int m, int m0, ExceptionTable et, CodeAttribute ca) { + cursor = cur; + mark = m; + mark0 = m0; + etable = et; // non null + line = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); + vars = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag); + types = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.typeTag); + stack = (StackMapTable)ca.getAttribute(StackMapTable.tag); + stack2 = (StackMap)ca.getAttribute(StackMap.tag); + } + + void shiftPc(int where, int gapLength, boolean exclusive) throws BadBytecode { + if (where < cursor || (where == cursor && exclusive)) + cursor += gapLength; + + if (where < mark || (where == mark && exclusive)) + mark += gapLength; + + if (where < mark0 || (where == mark0 && exclusive)) + mark0 += gapLength; + + etable.shiftPc(where, gapLength, exclusive); + if (line != null) + line.shiftPc(where, gapLength, exclusive); + + if (vars != null) + vars.shiftPc(where, gapLength, exclusive); + + if (types != null) + types.shiftPc(where, gapLength, exclusive); + + if (stack != null) + stack.shiftPc(where, gapLength, exclusive); + + if (stack2 != null) + stack2.shiftPc(where, gapLength, exclusive); + } + + void shiftForSwitch(int where, int gapLength) throws BadBytecode { + if (stack != null) + stack.shiftForSwitch(where, gapLength); + + if (stack2 != null) + stack2.shiftForSwitch(where, gapLength); + } + } + + /* + * This method is called from CodeAttribute.LdcEntry.doit(). + */ + static byte[] changeLdcToLdcW(byte[] code, ExceptionTable etable, + CodeAttribute ca, CodeAttribute.LdcEntry ldcs) + throws BadBytecode + { + Pointers pointers = new Pointers(0, 0, 0, etable, ca); + ArrayList jumps = makeJumpList(code, code.length, pointers); + while (ldcs != null) { + addLdcW(ldcs, jumps); + ldcs = ldcs.next; + } + + byte[] r = insertGap2w(code, 0, 0, false, jumps, pointers); + return r; + } + + private static void addLdcW(CodeAttribute.LdcEntry ldcs, ArrayList jumps) { + int where = ldcs.where; + LdcW ldcw = new LdcW(where, ldcs.index); + int s = jumps.size(); + for (int i = 0; i < s; i++) + if (where < ((Branch)jumps.get(i)).orgPos) { + jumps.add(i, ldcw); + return; + } + + jumps.add(ldcw); + } + + /* + * insertGapCore0w() can handle a long code sequence more than 32K. + * It guarantees that the length of the inserted gap (NOPs) is equal to + * gapLength. No other NOPs except some NOPs following TABLESWITCH or + * LOOKUPSWITCH will not be inserted. + * + * Note: currentPos might be moved. + * + * @param where It must indicate the first byte of an opcode. + * @param newWhere It contains the updated index of the position where a gap + * is inserted and the length of the gap. + * It must not be null. + */ + private byte[] insertGapCore0w(byte[] code, int where, int gapLength, boolean exclusive, + ExceptionTable etable, CodeAttribute ca, Gap newWhere) + throws BadBytecode + { + if (gapLength <= 0) + return code; + + Pointers pointers = new Pointers(currentPos, mark, where, etable, ca); + ArrayList jumps = makeJumpList(code, code.length, pointers); + byte[] r = insertGap2w(code, where, gapLength, exclusive, jumps, pointers); + currentPos = pointers.cursor; + mark = pointers.mark; + int where2 = pointers.mark0; + if (where2 == currentPos && !exclusive) + currentPos += gapLength; + + if (exclusive) + where2 -= gapLength; + + newWhere.position = where2; + newWhere.length = gapLength; + return r; + } + + private static byte[] insertGap2w(byte[] code, int where, int gapLength, + boolean exclusive, ArrayList jumps, Pointers ptrs) + throws BadBytecode + { + int n = jumps.size(); + if (gapLength > 0) { + ptrs.shiftPc(where, gapLength, exclusive); + for (int i = 0; i < n; i++) + ((Branch)jumps.get(i)).shift(where, gapLength, exclusive); + } + + boolean unstable = true; + do { + while (unstable) { + unstable = false; + for (int i = 0; i < n; i++) { + Branch b = (Branch)jumps.get(i); + if (b.expanded()) { + unstable = true; + int p = b.pos; + int delta = b.deltaSize(); + ptrs.shiftPc(p, delta, false); + for (int j = 0; j < n; j++) + ((Branch)jumps.get(j)).shift(p, delta, false); + } + } + } + + for (int i = 0; i < n; i++) { + Branch b = (Branch)jumps.get(i); + int diff = b.gapChanged(); + if (diff > 0) { + unstable = true; + int p = b.pos; + ptrs.shiftPc(p, diff, false); + for (int j = 0; j < n; j++) + ((Branch)jumps.get(j)).shift(p, diff, false); + } + } + } while (unstable); + + return makeExapndedCode(code, jumps, where, gapLength); + } + + private static ArrayList makeJumpList(byte[] code, int endPos, Pointers ptrs) + throws BadBytecode + { + ArrayList jumps = new ArrayList(); + int nextPos; + for (int i = 0; i < endPos; i = nextPos) { + nextPos = nextOpcode(code, i); + int inst = code[i] & 0xff; + // if, if_icmp, if_acmp, goto, jsr + if ((153 <= inst && inst <= 168) + || inst == IFNULL || inst == IFNONNULL) { + /* 2bytes *signed* offset */ + int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff); + Branch b; + if (inst == GOTO || inst == JSR) + b = new Jump16(i, offset); + else + b = new If16(i, offset); + + jumps.add(b); + } + else if (inst == GOTO_W || inst == JSR_W) { + /* 4bytes offset */ + int offset = ByteArray.read32bit(code, i + 1); + jumps.add(new Jump32(i, offset)); + } + else if (inst == TABLESWITCH) { + int i2 = (i & ~3) + 4; // 0-3 byte padding + int defaultbyte = ByteArray.read32bit(code, i2); + int lowbyte = ByteArray.read32bit(code, i2 + 4); + int highbyte = ByteArray.read32bit(code, i2 + 8); + int i0 = i2 + 12; + int size = highbyte - lowbyte + 1; + int[] offsets = new int[size]; + for (int j = 0; j < size; j++) { + offsets[j] = ByteArray.read32bit(code, i0); + i0 += 4; + } + + jumps.add(new Table(i, defaultbyte, lowbyte, highbyte, offsets, ptrs)); + } + else if (inst == LOOKUPSWITCH) { + int i2 = (i & ~3) + 4; // 0-3 byte padding + int defaultbyte = ByteArray.read32bit(code, i2); + int npairs = ByteArray.read32bit(code, i2 + 4); + int i0 = i2 + 8; + int[] matches = new int[npairs]; + int[] offsets = new int[npairs]; + for (int j = 0; j < npairs; j++) { + matches[j] = ByteArray.read32bit(code, i0); + offsets[j] = ByteArray.read32bit(code, i0 + 4); + i0 += 8; + } + + jumps.add(new Lookup(i, defaultbyte, matches, offsets, ptrs)); + } + } + + return jumps; + } + + private static byte[] makeExapndedCode(byte[] code, ArrayList jumps, + int where, int gapLength) + throws BadBytecode + { + int n = jumps.size(); + int size = code.length + gapLength; + for (int i = 0; i < n; i++) { + Branch b = (Branch)jumps.get(i); + size += b.deltaSize(); + } + + byte[] newcode = new byte[size]; + int src = 0, dest = 0, bindex = 0; + int len = code.length; + Branch b; + int bpos; + if (0 < n) { + b = (Branch)jumps.get(0); + bpos = b.orgPos; + } + else { + b = null; + bpos = len; // src will be never equal to bpos + } + + while (src < len) { + if (src == where) { + int pos2 = dest + gapLength; + while (dest < pos2) + newcode[dest++] = NOP; + } + + if (src != bpos) + newcode[dest++] = code[src++]; + else { + int s = b.write(src, code, dest, newcode); + src += s; + dest += s + b.deltaSize(); + if (++bindex < n) { + b = (Branch)jumps.get(bindex); + bpos = b.orgPos; + } + else { + b = null; + bpos = len; + } + } + } + + return newcode; + } + + static abstract class Branch { + int pos, orgPos; + Branch(int p) { pos = orgPos = p; } + void shift(int where, int gapLength, boolean exclusive) { + if (where < pos || (where == pos && exclusive)) + pos += gapLength; + } + + static int shiftOffset(int i, int offset, int where, + int gapLength, boolean exclusive) { + int target = i + offset; + if (i < where) { + if (where < target || (exclusive && where == target)) + offset += gapLength; + } + else if (i == where) { + // This code is different from the code in CodeIterator#newOffset(). + // see JASSIST-124. + if (target < where && exclusive) + offset -= gapLength; + else if (where < target && !exclusive) + offset += gapLength; + } + else + if (target < where || (!exclusive && where == target)) + offset -= gapLength; + + return offset; + } + + boolean expanded() { return false; } + int gapChanged() { return 0; } + int deltaSize() { return 0; } // newSize - oldSize + + // This returns the original instruction size. + abstract int write(int srcPos, byte[] code, int destPos, byte[] newcode) throws BadBytecode; + } + + /* used by changeLdcToLdcW() and CodeAttribute.LdcEntry. + */ + static class LdcW extends Branch { + int index; + boolean state; + LdcW(int p, int i) { + super(p); + index = i; + state = true; + } + + boolean expanded() { + if (state) { + state = false; + return true; + } + else + return false; + } + + int deltaSize() { return 1; } + + int write(int srcPos, byte[] code, int destPos, byte[] newcode) { + newcode[destPos] = LDC_W; + ByteArray.write16bit(index, newcode, destPos + 1); + return 2; + } + } + + static abstract class Branch16 extends Branch { + int offset; + int state; + static final int BIT16 = 0; + static final int EXPAND = 1; + static final int BIT32 = 2; + + Branch16(int p, int off) { + super(p); + offset = off; + state = BIT16; + } + + void shift(int where, int gapLength, boolean exclusive) { + offset = shiftOffset(pos, offset, where, gapLength, exclusive); + super.shift(where, gapLength, exclusive); + if (state == BIT16) + if (offset < Short.MIN_VALUE || Short.MAX_VALUE < offset) + state = EXPAND; + } + + boolean expanded() { + if (state == EXPAND) { + state = BIT32; + return true; + } + else + return false; + } + + abstract int deltaSize(); + abstract void write32(int src, byte[] code, int dest, byte[] newcode); + + int write(int src, byte[] code, int dest, byte[] newcode) { + if (state == BIT32) + write32(src, code, dest, newcode); + else { + newcode[dest] = code[src]; + ByteArray.write16bit(offset, newcode, dest + 1); + } + + return 3; + } + } + + // GOTO or JSR + static class Jump16 extends Branch16 { + Jump16(int p, int off) { + super(p, off); + } + + int deltaSize() { + return state == BIT32 ? 2 : 0; + } + + void write32(int src, byte[] code, int dest, byte[] newcode) { + newcode[dest] = (byte)(((code[src] & 0xff) == GOTO) ? GOTO_W : JSR_W); + ByteArray.write32bit(offset, newcode, dest + 1); + } + } + + // if, if_icmp, or if_acmp + static class If16 extends Branch16 { + If16(int p, int off) { + super(p, off); + } + + int deltaSize() { + return state == BIT32 ? 5 : 0; + } + + void write32(int src, byte[] code, int dest, byte[] newcode) { + newcode[dest] = (byte)opcode(code[src] & 0xff); + newcode[dest + 1] = 0; + newcode[dest + 2] = 8; // branch_offset = 8 + newcode[dest + 3] = (byte)GOTO_W; + ByteArray.write32bit(offset - 3, newcode, dest + 4); + } + + int opcode(int op) { + if (op == IFNULL) + return IFNONNULL; + else if (op == IFNONNULL) + return IFNULL; + else { + if (((op - IFEQ) & 1) == 0) + return op + 1; + else + return op - 1; + } + } + } + + static class Jump32 extends Branch { + int offset; + + Jump32(int p, int off) { + super(p); + offset = off; + } + + void shift(int where, int gapLength, boolean exclusive) { + offset = shiftOffset(pos, offset, where, gapLength, exclusive); + super.shift(where, gapLength, exclusive); + } + + int write(int src, byte[] code, int dest, byte[] newcode) { + newcode[dest] = code[src]; + ByteArray.write32bit(offset, newcode, dest + 1); + return 5; + } + } + + static abstract class Switcher extends Branch { + int gap, defaultByte; + int[] offsets; + Pointers pointers; + + Switcher(int pos, int defaultByte, int[] offsets, Pointers ptrs) { + super(pos); + this.gap = 3 - (pos & 3); + this.defaultByte = defaultByte; + this.offsets = offsets; + this.pointers = ptrs; + } + + void shift(int where, int gapLength, boolean exclusive) { + int p = pos; + defaultByte = shiftOffset(p, defaultByte, where, gapLength, exclusive); + int num = offsets.length; + for (int i = 0; i < num; i++) + offsets[i] = shiftOffset(p, offsets[i], where, gapLength, exclusive); + + super.shift(where, gapLength, exclusive); + } + + int gapChanged() { + int newGap = 3 - (pos & 3); + if (newGap > gap) { + int diff = newGap - gap; + gap = newGap; + return diff; + } + + return 0; + } + + int deltaSize() { + return gap - (3 - (orgPos & 3)); + } + + int write(int src, byte[] code, int dest, byte[] newcode) throws BadBytecode { + int padding = 3 - (pos & 3); + int nops = gap - padding; + int bytecodeSize = 5 + (3 - (orgPos & 3)) + tableSize(); + if (nops > 0) + adjustOffsets(bytecodeSize, nops); + + newcode[dest++] = code[src]; + while (padding-- > 0) + newcode[dest++] = 0; + + ByteArray.write32bit(defaultByte, newcode, dest); + int size = write2(dest + 4, newcode); + dest += size + 4; + while (nops-- > 0) + newcode[dest++] = NOP; + + return 5 + (3 - (orgPos & 3)) + size; + } + + abstract int write2(int dest, byte[] newcode); + abstract int tableSize(); + + /* If the new bytecode size is shorter than the original, some NOPs + * are appended after this branch instruction (tableswitch or + * lookupswitch) to fill the gap. + * This method changes a branch offset to point to the first NOP + * if the offset originally points to the bytecode next to this + * branch instruction. Otherwise, the bytecode would contain + * dead code. It complicates the generation of StackMap and + * StackMapTable. + */ + void adjustOffsets(int size, int nops) throws BadBytecode { + pointers.shiftForSwitch(pos + size, nops); + if (defaultByte == size) + defaultByte -= nops; + + for (int i = 0; i < offsets.length; i++) + if (offsets[i] == size) + offsets[i] -= nops; + } + } + + static class Table extends Switcher { + int low, high; + + Table(int pos, int defaultByte, int low, int high, int[] offsets, Pointers ptrs) { + super(pos, defaultByte, offsets, ptrs); + this.low = low; + this.high = high; + } + + int write2(int dest, byte[] newcode) { + ByteArray.write32bit(low, newcode, dest); + ByteArray.write32bit(high, newcode, dest + 4); + int n = offsets.length; + dest += 8; + for (int i = 0; i < n; i++) { + ByteArray.write32bit(offsets[i], newcode, dest); + dest += 4; + } + + return 8 + 4 * n; + } + + int tableSize() { return 8 + 4 * offsets.length; } + } + + static class Lookup extends Switcher { + int[] matches; + + Lookup(int pos, int defaultByte, int[] matches, int[] offsets, Pointers ptrs) { + super(pos, defaultByte, offsets, ptrs); + this.matches = matches; + } + + int write2(int dest, byte[] newcode) { + int n = matches.length; + ByteArray.write32bit(n, newcode, dest); + dest += 4; + for (int i = 0; i < n; i++) { + ByteArray.write32bit(matches[i], newcode, dest); + ByteArray.write32bit(offsets[i], newcode, dest + 4); + dest += 8; + } + + return 4 + 8 * n; + } + + int tableSize() { return 4 + 8 * matches.length; } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstPool.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstPool.java new file mode 100644 index 0000000..a7d94ae --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstPool.java @@ -0,0 +1,1995 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.wenshuo.agent.javassist.CtClass; + +/** + * Constant pool table. + */ +public final class ConstPool { + LongVector items; + int numOfItems; + int thisClassInfo; + HashMap itemsCache; + + /** + * CONSTANT_Class + */ + public static final int CONST_Class = ClassInfo.tag; + + /** + * CONSTANT_Fieldref + */ + public static final int CONST_Fieldref = FieldrefInfo.tag; + + /** + * CONSTANT_Methodref + */ + public static final int CONST_Methodref = MethodrefInfo.tag; + + /** + * CONSTANT_InterfaceMethodref + */ + public static final int CONST_InterfaceMethodref + = InterfaceMethodrefInfo.tag; + + /** + * CONSTANT_String + */ + public static final int CONST_String = StringInfo.tag; + + /** + * CONSTANT_Integer + */ + public static final int CONST_Integer = IntegerInfo.tag; + + /** + * CONSTANT_Float + */ + public static final int CONST_Float = FloatInfo.tag; + + /** + * CONSTANT_Long + */ + public static final int CONST_Long = LongInfo.tag; + + /** + * CONSTANT_Double + */ + public static final int CONST_Double = DoubleInfo.tag; + + /** + * CONSTANT_NameAndType + */ + public static final int CONST_NameAndType = NameAndTypeInfo.tag; + + /** + * CONSTANT_Utf8 + */ + public static final int CONST_Utf8 = Utf8Info.tag; + + /** + * CONSTANT_MethodHandle + */ + public static final int CONST_MethodHandle = MethodHandleInfo.tag; + + /** + * CONSTANT_MethodHandle + */ + public static final int CONST_MethodType = MethodTypeInfo.tag; + + /** + * CONSTANT_MethodHandle + */ + public static final int CONST_InvokeDynamic = InvokeDynamicInfo.tag; + + /** + * Represents the class using this constant pool table. + */ + public static final CtClass THIS = null; + + /** + * reference_kind of CONSTANT_MethodHandle_info. + */ + public static final int REF_getField = 1; + + /** + * reference_kind of CONSTANT_MethodHandle_info. + */ + public static final int REF_getStatic = 2; + + /** + * reference_kind of CONSTANT_MethodHandle_info. + */ + public static final int REF_putField = 3; + + /** + * reference_kind of CONSTANT_MethodHandle_info. + */ + public static final int REF_putStatic = 4; + + /** + * reference_kind of CONSTANT_MethodHandle_info. + */ + public static final int REF_invokeVirtual = 5; + + /** + * reference_kind of CONSTANT_MethodHandle_info. + */ + public static final int REF_invokeStatic = 6; + + /** + * reference_kind of CONSTANT_MethodHandle_info. + */ + public static final int REF_invokeSpecial = 7; + + /** + * reference_kind of CONSTANT_MethodHandle_info. + */ + public static final int REF_newInvokeSpecial = 8; + + /** + * reference_kind of CONSTANT_MethodHandle_info. + */ + public static final int REF_invokeInterface = 9; + + /** + * Constructs a constant pool table. + * + * @param thisclass the name of the class using this constant + * pool table + */ + public ConstPool(String thisclass) { + items = new LongVector(); + itemsCache = null; + numOfItems = 0; + addItem0(null); // index 0 is reserved by the JVM. + thisClassInfo = addClassInfo(thisclass); + } + + /** + * Constructs a constant pool table from the given byte stream. + * + * @param in byte stream. + */ + public ConstPool(DataInputStream in) throws IOException { + itemsCache = null; + thisClassInfo = 0; + /* read() initializes items and numOfItems, and do addItem(null). + */ + read(in); + } + + void prune() { + itemsCache = null; + } + + /** + * Returns the number of entries in this table. + */ + public int getSize() { + return numOfItems; + } + + /** + * Returns the name of the class using this constant pool table. + */ + public String getClassName() { + return getClassInfo(thisClassInfo); + } + + /** + * Returns the index of CONSTANT_Class_info structure + * specifying the class using this constant pool table. + */ + public int getThisClassInfo() { + return thisClassInfo; + } + + void setThisClassInfo(int i) { + thisClassInfo = i; + } + + ConstInfo getItem(int n) { + return items.elementAt(n); + } + + /** + * Returns the tag field of the constant pool table + * entry at the given index. + */ + public int getTag(int index) { + return getItem(index).getTag(); + } + + /** + * Reads CONSTANT_Class_info structure + * at the given index. + * + * @return a fully-qualified class or interface name specified + * by name_index. If the type is an array + * type, this method returns an encoded name like + * [Ljava.lang.Object; (note that the separators + * are not slashes but dots). + * @see javassist.ClassPool#getCtClass(String) + */ + public String getClassInfo(int index) { + ClassInfo c = (ClassInfo)getItem(index); + if (c == null) + return null; + else + return Descriptor.toJavaName(getUtf8Info(c.name)); + } + + /** + * Reads CONSTANT_Class_info structure + * at the given index. + * + * @return the descriptor of the type specified + * by name_index. + * @see javassist.ClassPool#getCtClass(String) + * @since 3.15 + */ + public String getClassInfoByDescriptor(int index) { + ClassInfo c = (ClassInfo)getItem(index); + if (c == null) + return null; + else { + String className = getUtf8Info(c.name); + if (className.charAt(0) == '[') + return className; + else + return Descriptor.of(className); + } + } + + /** + * Reads the name_index field of the + * CONSTANT_NameAndType_info structure + * at the given index. + */ + public int getNameAndTypeName(int index) { + NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index); + return ntinfo.memberName; + } + + /** + * Reads the descriptor_index field of the + * CONSTANT_NameAndType_info structure + * at the given index. + */ + public int getNameAndTypeDescriptor(int index) { + NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index); + return ntinfo.typeDescriptor; + } + + /** + * Reads the class_index field of the + * CONSTANT_Fieldref_info, + * CONSTANT_Methodref_info, + * or CONSTANT_Interfaceref_info, + * structure at the given index. + * + * @since 3.6 + */ + public int getMemberClass(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + return minfo.classIndex; + } + + /** + * Reads the name_and_type_index field of the + * CONSTANT_Fieldref_info, + * CONSTANT_Methodref_info, + * or CONSTANT_Interfaceref_info, + * structure at the given index. + * + * @since 3.6 + */ + public int getMemberNameAndType(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + return minfo.nameAndTypeIndex; + } + + /** + * Reads the class_index field of the + * CONSTANT_Fieldref_info structure + * at the given index. + */ + public int getFieldrefClass(int index) { + FieldrefInfo finfo = (FieldrefInfo)getItem(index); + return finfo.classIndex; + } + + /** + * Reads the class_index field of the + * CONSTANT_Fieldref_info structure + * at the given index. + * + * @return the name of the class at that class_index. + */ + public String getFieldrefClassName(int index) { + FieldrefInfo f = (FieldrefInfo)getItem(index); + if (f == null) + return null; + else + return getClassInfo(f.classIndex); + } + + /** + * Reads the name_and_type_index field of the + * CONSTANT_Fieldref_info structure + * at the given index. + */ + public int getFieldrefNameAndType(int index) { + FieldrefInfo finfo = (FieldrefInfo)getItem(index); + return finfo.nameAndTypeIndex; + } + + /** + * Reads the name_index field of the + * CONSTANT_NameAndType_info structure + * indirectly specified by the given index. + * + * @param index an index to a CONSTANT_Fieldref_info. + * @return the name of the field. + */ + public String getFieldrefName(int index) { + FieldrefInfo f = (FieldrefInfo)getItem(index); + if (f == null) + return null; + else { + NameAndTypeInfo n = (NameAndTypeInfo)getItem(f.nameAndTypeIndex); + if(n == null) + return null; + else + return getUtf8Info(n.memberName); + } + } + + /** + * Reads the descriptor_index field of the + * CONSTANT_NameAndType_info structure + * indirectly specified by the given index. + * + * @param index an index to a CONSTANT_Fieldref_info. + * @return the type descriptor of the field. + */ + public String getFieldrefType(int index) { + FieldrefInfo f = (FieldrefInfo)getItem(index); + if (f == null) + return null; + else { + NameAndTypeInfo n = (NameAndTypeInfo)getItem(f.nameAndTypeIndex); + if(n == null) + return null; + else + return getUtf8Info(n.typeDescriptor); + } + } + + /** + * Reads the class_index field of the + * CONSTANT_Methodref_info structure + * at the given index. + */ + public int getMethodrefClass(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + return minfo.classIndex; + } + + /** + * Reads the class_index field of the + * CONSTANT_Methodref_info structure + * at the given index. + * + * @return the name of the class at that class_index. + */ + public String getMethodrefClassName(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + if (minfo == null) + return null; + else + return getClassInfo(minfo.classIndex); + } + + /** + * Reads the name_and_type_index field of the + * CONSTANT_Methodref_info structure + * at the given index. + */ + public int getMethodrefNameAndType(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + return minfo.nameAndTypeIndex; + } + + /** + * Reads the name_index field of the + * CONSTANT_NameAndType_info structure + * indirectly specified by the given index. + * + * @param index an index to a CONSTANT_Methodref_info. + * @return the name of the method. + */ + public String getMethodrefName(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + if (minfo == null) + return null; + else { + NameAndTypeInfo n + = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); + if(n == null) + return null; + else + return getUtf8Info(n.memberName); + } + } + + /** + * Reads the descriptor_index field of the + * CONSTANT_NameAndType_info structure + * indirectly specified by the given index. + * + * @param index an index to a CONSTANT_Methodref_info. + * @return the descriptor of the method. + */ + public String getMethodrefType(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + if (minfo == null) + return null; + else { + NameAndTypeInfo n + = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); + if(n == null) + return null; + else + return getUtf8Info(n.typeDescriptor); + } + } + + /** + * Reads the class_index field of the + * CONSTANT_InterfaceMethodref_info structure + * at the given index. + */ + public int getInterfaceMethodrefClass(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + return minfo.classIndex; + } + + /** + * Reads the class_index field of the + * CONSTANT_InterfaceMethodref_info structure + * at the given index. + * + * @return the name of the class at that class_index. + */ + public String getInterfaceMethodrefClassName(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + return getClassInfo(minfo.classIndex); + } + + /** + * Reads the name_and_type_index field of the + * CONSTANT_InterfaceMethodref_info structure + * at the given index. + */ + public int getInterfaceMethodrefNameAndType(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + return minfo.nameAndTypeIndex; + } + + /** + * Reads the name_index field of the + * CONSTANT_NameAndType_info structure + * indirectly specified by the given index. + * + * @param index an index to + * a CONSTANT_InterfaceMethodref_info. + * @return the name of the method. + */ + public String getInterfaceMethodrefName(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + if (minfo == null) + return null; + else { + NameAndTypeInfo n + = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); + if(n == null) + return null; + else + return getUtf8Info(n.memberName); + } + } + + /** + * Reads the descriptor_index field of the + * CONSTANT_NameAndType_info structure + * indirectly specified by the given index. + * + * @param index an index to + * a CONSTANT_InterfaceMethodref_info. + * @return the descriptor of the method. + */ + public String getInterfaceMethodrefType(int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + if (minfo == null) + return null; + else { + NameAndTypeInfo n + = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); + if(n == null) + return null; + else + return getUtf8Info(n.typeDescriptor); + } + } + /** + * Reads CONSTANT_Integer_info, _Float_info, + * _Long_info, _Double_info, or + * _String_info structure. + * These are used with the LDC instruction. + * + * @return a String value or a wrapped primitive-type + * value. + */ + public Object getLdcValue(int index) { + ConstInfo constInfo = this.getItem(index); + Object value = null; + if (constInfo instanceof StringInfo) + value = this.getStringInfo(index); + else if (constInfo instanceof FloatInfo) + value = new Float(getFloatInfo(index)); + else if (constInfo instanceof IntegerInfo) + value = new Integer(getIntegerInfo(index)); + else if (constInfo instanceof LongInfo) + value = new Long(getLongInfo(index)); + else if (constInfo instanceof DoubleInfo) + value = new Double(getDoubleInfo(index)); + else + value = null; + + return value; + } + + /** + * Reads CONSTANT_Integer_info structure + * at the given index. + * + * @return the value specified by this entry. + */ + public int getIntegerInfo(int index) { + IntegerInfo i = (IntegerInfo)getItem(index); + return i.value; + } + + /** + * Reads CONSTANT_Float_info structure + * at the given index. + * + * @return the value specified by this entry. + */ + public float getFloatInfo(int index) { + FloatInfo i = (FloatInfo)getItem(index); + return i.value; + } + + /** + * Reads CONSTANT_Long_info structure + * at the given index. + * + * @return the value specified by this entry. + */ + public long getLongInfo(int index) { + LongInfo i = (LongInfo)getItem(index); + return i.value; + } + + /** + * Reads CONSTANT_Double_info structure + * at the given index. + * + * @return the value specified by this entry. + */ + public double getDoubleInfo(int index) { + DoubleInfo i = (DoubleInfo)getItem(index); + return i.value; + } + + /** + * Reads CONSTANT_String_info structure + * at the given index. + * + * @return the string specified by string_index. + */ + public String getStringInfo(int index) { + StringInfo si = (StringInfo)getItem(index); + return getUtf8Info(si.string); + } + + /** + * Reads CONSTANT_utf8_info structure + * at the given index. + * + * @return the string specified by this entry. + */ + public String getUtf8Info(int index) { + Utf8Info utf = (Utf8Info)getItem(index); + return utf.string; + } + + /** + * Reads the reference_kind field of the + * CONSTANT_MethodHandle_info structure + * at the given index. + * + * @see #REF_getField + * @see #REF_getStatic + * @see #REF_invokeInterface + * @see #REF_invokeSpecial + * @see #REF_invokeStatic + * @see #REF_invokeVirtual + * @see #REF_newInvokeSpecial + * @see #REF_putField + * @see #REF_putStatic + * @since 3.17 + */ + public int getMethodHandleKind(int index) { + MethodHandleInfo mhinfo = (MethodHandleInfo)getItem(index); + return mhinfo.refKind; + } + + /** + * Reads the reference_index field of the + * CONSTANT_MethodHandle_info structure + * at the given index. + * + * @since 3.17 + */ + public int getMethodHandleIndex(int index) { + MethodHandleInfo mhinfo = (MethodHandleInfo)getItem(index); + return mhinfo.refIndex; + } + + /** + * Reads the descriptor_index field of the + * CONSTANT_MethodType_info structure + * at the given index. + * + * @since 3.17 + */ + public int getMethodTypeInfo(int index) { + MethodTypeInfo mtinfo = (MethodTypeInfo)getItem(index); + return mtinfo.descriptor; + } + + /** + * Reads the bootstrap_method_attr_index field of the + * CONSTANT_InvokeDynamic_info structure + * at the given index. + * + * @since 3.17 + */ + public int getInvokeDynamicBootstrap(int index) { + InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index); + return iv.bootstrap; + } + + /** + * Reads the name_and_type_index field of the + * CONSTANT_InvokeDynamic_info structure + * at the given index. + * + * @since 3.17 + */ + public int getInvokeDynamicNameAndType(int index) { + InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index); + return iv.nameAndType; + } + + /** + * Reads the descriptor_index field of the + * CONSTANT_NameAndType_info structure + * indirectly specified by the given index. + * + * @param index an index to a CONSTANT_InvokeDynamic_info. + * @return the descriptor of the method. + * @since 3.17 + */ + public String getInvokeDynamicType(int index) { + InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index); + if (iv == null) + return null; + else { + NameAndTypeInfo n = (NameAndTypeInfo)getItem(iv.nameAndType); + if(n == null) + return null; + else + return getUtf8Info(n.typeDescriptor); + } + } + + /** + * Determines whether CONSTANT_Methodref_info + * structure at the given index represents the constructor + * of the given class. + * + * @return the descriptor_index specifying + * the type descriptor of the that constructor. + * If it is not that constructor, + * isConstructor() returns 0. + */ + public int isConstructor(String classname, int index) { + return isMember(classname, MethodInfo.nameInit, index); + } + + /** + * Determines whether CONSTANT_Methodref_info, + * CONSTANT_Fieldref_info, or + * CONSTANT_InterfaceMethodref_info structure + * at the given index represents the member with the specified + * name and declaring class. + * + * @param classname the class declaring the member + * @param membername the member name + * @param index the index into the constant pool table + * + * @return the descriptor_index specifying + * the type descriptor of that member. + * If it is not that member, + * isMember() returns 0. + */ + public int isMember(String classname, String membername, int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + if (getClassInfo(minfo.classIndex).equals(classname)) { + NameAndTypeInfo ntinfo + = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); + if (getUtf8Info(ntinfo.memberName).equals(membername)) + return ntinfo.typeDescriptor; + } + + return 0; // false + } + + /** + * Determines whether CONSTANT_Methodref_info, + * CONSTANT_Fieldref_info, or + * CONSTANT_InterfaceMethodref_info structure + * at the given index has the name and the descriptor + * given as the arguments. + * + * @param membername the member name + * @param desc the descriptor of the member. + * @param index the index into the constant pool table + * + * @return the name of the target class specified by + * the ..._info structure + * at index. + * Otherwise, null if that structure does not + * match the given member name and descriptor. + */ + public String eqMember(String membername, String desc, int index) { + MemberrefInfo minfo = (MemberrefInfo)getItem(index); + NameAndTypeInfo ntinfo + = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); + if (getUtf8Info(ntinfo.memberName).equals(membername) + && getUtf8Info(ntinfo.typeDescriptor).equals(desc)) + return getClassInfo(minfo.classIndex); + else + return null; // false + } + + private int addItem0(ConstInfo info) { + items.addElement(info); + return numOfItems++; + } + + private int addItem(ConstInfo info) { + if (itemsCache == null) + itemsCache = makeItemsCache(items); + + ConstInfo found = (ConstInfo)itemsCache.get(info); + if (found != null) + return found.index; + else { + items.addElement(info); + itemsCache.put(info, info); + return numOfItems++; + } + } + + /** + * Copies the n-th item in this ConstPool object into the destination + * ConstPool object. + * The class names that the item refers to are renamed according + * to the given map. + * + * @param n the n-th item + * @param dest destination constant pool table + * @param classnames the map or null. + * @return the index of the copied item into the destination ClassPool. + */ + public int copy(int n, ConstPool dest, Map classnames) { + if (n == 0) + return 0; + + ConstInfo info = getItem(n); + return info.copy(this, dest, classnames); + } + + int addConstInfoPadding() { + return addItem0(new ConstInfoPadding(numOfItems)); + } + + /** + * Adds a new CONSTANT_Class_info structure. + * + *

This also adds a CONSTANT_Utf8_info structure + * for storing the class name. + * + * @return the index of the added entry. + */ + public int addClassInfo(CtClass c) { + if (c == THIS) + return thisClassInfo; + else if (!c.isArray()) + return addClassInfo(c.getName()); + else { + // an array type is recorded in the hashtable with + // the key "[L;" instead of "". + // + // note: toJvmName(toJvmName(c)) is equal to toJvmName(c). + + return addClassInfo(Descriptor.toJvmName(c)); + } + } + + /** + * Adds a new CONSTANT_Class_info structure. + * + *

This also adds a CONSTANT_Utf8_info structure + * for storing the class name. + * + * @param qname a fully-qualified class name + * (or the JVM-internal representation of that name). + * @return the index of the added entry. + */ + public int addClassInfo(String qname) { + int utf8 = addUtf8Info(Descriptor.toJvmName(qname)); + return addItem(new ClassInfo(utf8, numOfItems)); + } + + /** + * Adds a new CONSTANT_NameAndType_info structure. + * + *

This also adds CONSTANT_Utf8_info structures. + * + * @param name name_index + * @param type descriptor_index + * @return the index of the added entry. + */ + public int addNameAndTypeInfo(String name, String type) { + return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type)); + } + + /** + * Adds a new CONSTANT_NameAndType_info structure. + * + * @param name name_index + * @param type descriptor_index + * @return the index of the added entry. + */ + public int addNameAndTypeInfo(int name, int type) { + return addItem(new NameAndTypeInfo(name, type, numOfItems)); + } + + /** + * Adds a new CONSTANT_Fieldref_info structure. + * + *

This also adds a new CONSTANT_NameAndType_info + * structure. + * + * @param classInfo class_index + * @param name name_index + * of CONSTANT_NameAndType_info. + * @param type descriptor_index + * of CONSTANT_NameAndType_info. + * @return the index of the added entry. + */ + public int addFieldrefInfo(int classInfo, String name, String type) { + int nt = addNameAndTypeInfo(name, type); + return addFieldrefInfo(classInfo, nt); + } + + /** + * Adds a new CONSTANT_Fieldref_info structure. + * + * @param classInfo class_index + * @param nameAndTypeInfo name_and_type_index. + * @return the index of the added entry. + */ + public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) { + return addItem(new FieldrefInfo(classInfo, nameAndTypeInfo, numOfItems)); + } + + /** + * Adds a new CONSTANT_Methodref_info structure. + * + *

This also adds a new CONSTANT_NameAndType_info + * structure. + * + * @param classInfo class_index + * @param name name_index + * of CONSTANT_NameAndType_info. + * @param type descriptor_index + * of CONSTANT_NameAndType_info. + * @return the index of the added entry. + */ + public int addMethodrefInfo(int classInfo, String name, String type) { + int nt = addNameAndTypeInfo(name, type); + return addMethodrefInfo(classInfo, nt); + } + + /** + * Adds a new CONSTANT_Methodref_info structure. + * + * @param classInfo class_index + * @param nameAndTypeInfo name_and_type_index. + * @return the index of the added entry. + */ + public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) { + return addItem(new MethodrefInfo(classInfo, nameAndTypeInfo, numOfItems)); + } + + /** + * Adds a new CONSTANT_InterfaceMethodref_info + * structure. + * + *

This also adds a new CONSTANT_NameAndType_info + * structure. + * + * @param classInfo class_index + * @param name name_index + * of CONSTANT_NameAndType_info. + * @param type descriptor_index + * of CONSTANT_NameAndType_info. + * @return the index of the added entry. + */ + public int addInterfaceMethodrefInfo(int classInfo, String name, + String type) { + int nt = addNameAndTypeInfo(name, type); + return addInterfaceMethodrefInfo(classInfo, nt); + } + + /** + * Adds a new CONSTANT_InterfaceMethodref_info + * structure. + * + * @param classInfo class_index + * @param nameAndTypeInfo name_and_type_index. + * @return the index of the added entry. + */ + public int addInterfaceMethodrefInfo(int classInfo, + int nameAndTypeInfo) { + return addItem(new InterfaceMethodrefInfo(classInfo, nameAndTypeInfo, + numOfItems)); + } + + /** + * Adds a new CONSTANT_String_info + * structure. + * + *

This also adds a new CONSTANT_Utf8_info + * structure. + * + * @return the index of the added entry. + */ + public int addStringInfo(String str) { + int utf = addUtf8Info(str); + return addItem(new StringInfo(utf, numOfItems)); + } + + /** + * Adds a new CONSTANT_Integer_info + * structure. + * + * @return the index of the added entry. + */ + public int addIntegerInfo(int i) { + return addItem(new IntegerInfo(i, numOfItems)); + } + + /** + * Adds a new CONSTANT_Float_info + * structure. + * + * @return the index of the added entry. + */ + public int addFloatInfo(float f) { + return addItem(new FloatInfo(f, numOfItems)); + } + + /** + * Adds a new CONSTANT_Long_info + * structure. + * + * @return the index of the added entry. + */ + public int addLongInfo(long l) { + int i = addItem(new LongInfo(l, numOfItems)); + if (i == numOfItems - 1) // if not existing + addConstInfoPadding(); + + return i; + } + + /** + * Adds a new CONSTANT_Double_info + * structure. + * + * @return the index of the added entry. + */ + public int addDoubleInfo(double d) { + int i = addItem(new DoubleInfo(d, numOfItems)); + if (i == numOfItems - 1) // if not existing + addConstInfoPadding(); + + return i; + } + + /** + * Adds a new CONSTANT_Utf8_info + * structure. + * + * @return the index of the added entry. + */ + public int addUtf8Info(String utf8) { + return addItem(new Utf8Info(utf8, numOfItems)); + } + + /** + * Adds a new CONSTANT_MethodHandle_info + * structure. + * + * @param kind reference_kind + * such as {@link #REF_invokeStatic REF_invokeStatic}. + * @param index reference_index. + * @return the index of the added entry. + * + * @since 3.17 + */ + public int addMethodHandleInfo(int kind, int index) { + return addItem(new MethodHandleInfo(kind, index, numOfItems)); + } + + /** + * Adds a new CONSTANT_MethodType_info + * structure. + * + * @param desc descriptor_index. + * @return the index of the added entry. + * + * @since 3.17 + */ + public int addMethodTypeInfo(int desc) { + return addItem(new MethodTypeInfo(desc, numOfItems)); + } + + /** + * Adds a new CONSTANT_InvokeDynamic_info + * structure. + * + * @param bootstrap bootstrap_method_attr_index. + * @param nameAndType name_and_type_index. + * @return the index of the added entry. + * + * @since 3.17 + */ + public int addInvokeDynamicInfo(int bootstrap, int nameAndType) { + return addItem(new InvokeDynamicInfo(bootstrap, nameAndType, numOfItems)); + } + + /** + * Get all the class names. + * + * @return a set of class names (String objects). + */ + public Set getClassNames() { + HashSet result = new HashSet(); + LongVector v = items; + int size = numOfItems; + for (int i = 1; i < size; ++i) { + String className = v.elementAt(i).getClassName(this); + if (className != null) + result.add(className); + } + return result; + } + + /** + * Replaces all occurrences of a class name. + * + * @param oldName the replaced name (JVM-internal representation). + * @param newName the substituted name (JVM-internal representation). + */ + public void renameClass(String oldName, String newName) { + LongVector v = items; + int size = numOfItems; + for (int i = 1; i < size; ++i) { + ConstInfo ci = v.elementAt(i); + ci.renameClass(this, oldName, newName, itemsCache); + } + } + + /** + * Replaces all occurrences of class names. + * + * @param classnames specifies pairs of replaced and substituted + * name. + */ + public void renameClass(Map classnames) { + LongVector v = items; + int size = numOfItems; + for (int i = 1; i < size; ++i) { + ConstInfo ci = v.elementAt(i); + ci.renameClass(this, classnames, itemsCache); + } + } + + private void read(DataInputStream in) throws IOException { + int n = in.readUnsignedShort(); + + items = new LongVector(n); + numOfItems = 0; + addItem0(null); // index 0 is reserved by the JVM. + + while (--n > 0) { // index 0 is reserved by JVM + int tag = readOne(in); + if ((tag == LongInfo.tag) || (tag == DoubleInfo.tag)) { + addConstInfoPadding(); + --n; + } + } + } + + private static HashMap makeItemsCache(LongVector items) { + HashMap cache = new HashMap(); + int i = 1; + while (true) { + ConstInfo info = items.elementAt(i++); + if (info == null) + break; + else + cache.put(info, info); + } + + return cache; + } + + private int readOne(DataInputStream in) throws IOException { + ConstInfo info; + int tag = in.readUnsignedByte(); + switch (tag) { + case Utf8Info.tag : // 1 + info = new Utf8Info(in, numOfItems); + break; + case IntegerInfo.tag : // 3 + info = new IntegerInfo(in, numOfItems); + break; + case FloatInfo.tag : // 4 + info = new FloatInfo(in, numOfItems); + break; + case LongInfo.tag : // 5 + info = new LongInfo(in, numOfItems); + break; + case DoubleInfo.tag : // 6 + info = new DoubleInfo(in, numOfItems); + break; + case ClassInfo.tag : // 7 + info = new ClassInfo(in, numOfItems); + break; + case StringInfo.tag : // 8 + info = new StringInfo(in, numOfItems); + break; + case FieldrefInfo.tag : // 9 + info = new FieldrefInfo(in, numOfItems); + break; + case MethodrefInfo.tag : // 10 + info = new MethodrefInfo(in, numOfItems); + break; + case InterfaceMethodrefInfo.tag : // 11 + info = new InterfaceMethodrefInfo(in, numOfItems); + break; + case NameAndTypeInfo.tag : // 12 + info = new NameAndTypeInfo(in, numOfItems); + break; + case MethodHandleInfo.tag : // 15 + info = new MethodHandleInfo(in, numOfItems); + break; + case MethodTypeInfo.tag : // 16 + info = new MethodTypeInfo(in, numOfItems); + break; + case InvokeDynamicInfo.tag : // 18 + info = new InvokeDynamicInfo(in, numOfItems); + break; + default : + throw new IOException("invalid constant type: " + tag + " at " + numOfItems); + } + + addItem0(info); + return tag; + } + + /** + * Writes the contents of the constant pool table. + */ + public void write(DataOutputStream out) throws IOException { + out.writeShort(numOfItems); + LongVector v = items; + int size = numOfItems; + for (int i = 1; i < size; ++i) + v.elementAt(i).write(out); + } + + /** + * Prints the contents of the constant pool table. + */ + public void print() { + print(new PrintWriter(System.out, true)); + } + + /** + * Prints the contents of the constant pool table. + */ + public void print(PrintWriter out) { + int size = numOfItems; + for (int i = 1; i < size; ++i) { + out.print(i); + out.print(" "); + items.elementAt(i).print(out); + } + } +} + +abstract class ConstInfo { + int index; + + public ConstInfo(int i) { index = i; } + + public abstract int getTag(); + + public String getClassName(ConstPool cp) { return null; } + public void renameClass(ConstPool cp, String oldName, String newName, HashMap cache) {} + public void renameClass(ConstPool cp, Map classnames, HashMap cache) {} + public abstract int copy(ConstPool src, ConstPool dest, Map classnames); + // ** classnames is a mapping between JVM names. + + public abstract void write(DataOutputStream out) throws IOException; + public abstract void print(PrintWriter out); + + public String toString() { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + PrintWriter out = new PrintWriter(bout); + print(out); + return bout.toString(); + } +} + +/* padding following DoubleInfo or LongInfo. + */ +class ConstInfoPadding extends ConstInfo { + public ConstInfoPadding(int i) { super(i); } + + public int getTag() { return 0; } + + public int copy(ConstPool src, ConstPool dest, Map map) { + return dest.addConstInfoPadding(); + } + + public void write(DataOutputStream out) throws IOException {} + + public void print(PrintWriter out) { + out.println("padding"); + } +} + +class ClassInfo extends ConstInfo { + static final int tag = 7; + int name; + + public ClassInfo(int className, int index) { + super(index); + name = className; + } + + public ClassInfo(DataInputStream in, int index) throws IOException { + super(index); + name = in.readUnsignedShort(); + } + + public int hashCode() { return name; } + + public boolean equals(Object obj) { + return obj instanceof ClassInfo && ((ClassInfo)obj).name == name; + } + + public int getTag() { return tag; } + + public String getClassName(ConstPool cp) { + return cp.getUtf8Info(name); + } + + public void renameClass(ConstPool cp, String oldName, String newName, HashMap cache) { + String nameStr = cp.getUtf8Info(name); + String newNameStr = null; + if (nameStr.equals(oldName)) + newNameStr = newName; + else if (nameStr.charAt(0) == '[') { + String s = Descriptor.rename(nameStr, oldName, newName); + if (nameStr != s) + newNameStr = s; + } + + if (newNameStr != null) + if (cache == null) + name = cp.addUtf8Info(newNameStr); + else { + cache.remove(this); + name = cp.addUtf8Info(newNameStr); + cache.put(this, this); + } + } + + public void renameClass(ConstPool cp, Map map, HashMap cache) { + String oldName = cp.getUtf8Info(name); + String newName = null; + if (oldName.charAt(0) == '[') { + String s = Descriptor.rename(oldName, map); + if (oldName != s) + newName = s; + } + else { + String s = (String)map.get(oldName); + if (s != null && !s.equals(oldName)) + newName = s; + } + + if (newName != null) { + if (cache == null) + name = cp.addUtf8Info(newName); + else { + cache.remove(this); + name = cp.addUtf8Info(newName); + cache.put(this, this); + } + } + } + + public int copy(ConstPool src, ConstPool dest, Map map) { + String classname = src.getUtf8Info(name); + if (map != null) { + String newname = (String)map.get(classname); + if (newname != null) + classname = newname; + } + + return dest.addClassInfo(classname); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeShort(name); + } + + public void print(PrintWriter out) { + out.print("Class #"); + out.println(name); + } +} + +class NameAndTypeInfo extends ConstInfo { + static final int tag = 12; + int memberName; + int typeDescriptor; + + public NameAndTypeInfo(int name, int type, int index) { + super(index); + memberName = name; + typeDescriptor = type; + } + + public NameAndTypeInfo(DataInputStream in, int index) throws IOException { + super(index); + memberName = in.readUnsignedShort(); + typeDescriptor = in.readUnsignedShort(); + } + + public int hashCode() { return (memberName << 16) ^ typeDescriptor; } + + public boolean equals(Object obj) { + if (obj instanceof NameAndTypeInfo) { + NameAndTypeInfo nti = (NameAndTypeInfo)obj; + return nti.memberName == memberName && nti.typeDescriptor == typeDescriptor; + } + else + return false; + } + + public int getTag() { return tag; } + + public void renameClass(ConstPool cp, String oldName, String newName, HashMap cache) { + String type = cp.getUtf8Info(typeDescriptor); + String type2 = Descriptor.rename(type, oldName, newName); + if (type != type2) + if (cache == null) + typeDescriptor = cp.addUtf8Info(type2); + else { + cache.remove(this); + typeDescriptor = cp.addUtf8Info(type2); + cache.put(this, this); + } + } + + public void renameClass(ConstPool cp, Map map, HashMap cache) { + String type = cp.getUtf8Info(typeDescriptor); + String type2 = Descriptor.rename(type, map); + if (type != type2) + if (cache == null) + typeDescriptor = cp.addUtf8Info(type2); + else { + cache.remove(this); + typeDescriptor = cp.addUtf8Info(type2); + cache.put(this, this); + } + } + + public int copy(ConstPool src, ConstPool dest, Map map) { + String mname = src.getUtf8Info(memberName); + String tdesc = src.getUtf8Info(typeDescriptor); + tdesc = Descriptor.rename(tdesc, map); + return dest.addNameAndTypeInfo(dest.addUtf8Info(mname), + dest.addUtf8Info(tdesc)); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeShort(memberName); + out.writeShort(typeDescriptor); + } + + public void print(PrintWriter out) { + out.print("NameAndType #"); + out.print(memberName); + out.print(", type #"); + out.println(typeDescriptor); + } +} + +abstract class MemberrefInfo extends ConstInfo { + int classIndex; + int nameAndTypeIndex; + + public MemberrefInfo(int cindex, int ntindex, int thisIndex) { + super(thisIndex); + classIndex = cindex; + nameAndTypeIndex = ntindex; + } + + public MemberrefInfo(DataInputStream in, int thisIndex) throws IOException { + super(thisIndex); + classIndex = in.readUnsignedShort(); + nameAndTypeIndex = in.readUnsignedShort(); + } + + public int hashCode() { return (classIndex << 16) ^ nameAndTypeIndex; } + + public boolean equals(Object obj) { + if (obj instanceof MemberrefInfo) { + MemberrefInfo mri = (MemberrefInfo)obj; + return mri.classIndex == classIndex && mri.nameAndTypeIndex == nameAndTypeIndex + && mri.getClass() == this.getClass(); + } + else + return false; + } + + public int copy(ConstPool src, ConstPool dest, Map map) { + int classIndex2 = src.getItem(classIndex).copy(src, dest, map); + int ntIndex2 = src.getItem(nameAndTypeIndex).copy(src, dest, map); + return copy2(dest, classIndex2, ntIndex2); + } + + abstract protected int copy2(ConstPool dest, int cindex, int ntindex); + + public void write(DataOutputStream out) throws IOException { + out.writeByte(getTag()); + out.writeShort(classIndex); + out.writeShort(nameAndTypeIndex); + } + + public void print(PrintWriter out) { + out.print(getTagName() + " #"); + out.print(classIndex); + out.print(", name&type #"); + out.println(nameAndTypeIndex); + } + + public abstract String getTagName(); +} + +class FieldrefInfo extends MemberrefInfo { + static final int tag = 9; + + public FieldrefInfo(int cindex, int ntindex, int thisIndex) { + super(cindex, ntindex, thisIndex); + } + + public FieldrefInfo(DataInputStream in, int thisIndex) throws IOException { + super(in, thisIndex); + } + + public int getTag() { return tag; } + + public String getTagName() { return "Field"; } + + protected int copy2(ConstPool dest, int cindex, int ntindex) { + return dest.addFieldrefInfo(cindex, ntindex); + } +} + +class MethodrefInfo extends MemberrefInfo { + static final int tag = 10; + + public MethodrefInfo(int cindex, int ntindex, int thisIndex) { + super(cindex, ntindex, thisIndex); + } + + public MethodrefInfo(DataInputStream in, int thisIndex) throws IOException { + super(in, thisIndex); + } + + public int getTag() { return tag; } + + public String getTagName() { return "Method"; } + + protected int copy2(ConstPool dest, int cindex, int ntindex) { + return dest.addMethodrefInfo(cindex, ntindex); + } +} + +class InterfaceMethodrefInfo extends MemberrefInfo { + static final int tag = 11; + + public InterfaceMethodrefInfo(int cindex, int ntindex, int thisIndex) { + super(cindex, ntindex, thisIndex); + } + + public InterfaceMethodrefInfo(DataInputStream in, int thisIndex) throws IOException { + super(in, thisIndex); + } + + public int getTag() { return tag; } + + public String getTagName() { return "Interface"; } + + protected int copy2(ConstPool dest, int cindex, int ntindex) { + return dest.addInterfaceMethodrefInfo(cindex, ntindex); + } +} + +class StringInfo extends ConstInfo { + static final int tag = 8; + int string; + + public StringInfo(int str, int index) { + super(index); + string = str; + } + + public StringInfo(DataInputStream in, int index) throws IOException { + super(index); + string = in.readUnsignedShort(); + } + + public int hashCode() { return string; } + + public boolean equals(Object obj) { + return obj instanceof StringInfo && ((StringInfo)obj).string == string; + } + + public int getTag() { return tag; } + + public int copy(ConstPool src, ConstPool dest, Map map) { + return dest.addStringInfo(src.getUtf8Info(string)); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeShort(string); + } + + public void print(PrintWriter out) { + out.print("String #"); + out.println(string); + } +} + +class IntegerInfo extends ConstInfo { + static final int tag = 3; + int value; + + public IntegerInfo(int v, int index) { + super(index); + value = v; + } + + public IntegerInfo(DataInputStream in, int index) throws IOException { + super(index); + value = in.readInt(); + } + + public int hashCode() { return value; } + + public boolean equals(Object obj) { + return obj instanceof IntegerInfo && ((IntegerInfo)obj).value == value; + } + + public int getTag() { return tag; } + + public int copy(ConstPool src, ConstPool dest, Map map) { + return dest.addIntegerInfo(value); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeInt(value); + } + + public void print(PrintWriter out) { + out.print("Integer "); + out.println(value); + } +} + +class FloatInfo extends ConstInfo { + static final int tag = 4; + float value; + + public FloatInfo(float f, int index) { + super(index); + value = f; + } + + public FloatInfo(DataInputStream in, int index) throws IOException { + super(index); + value = in.readFloat(); + } + + public int hashCode() { return Float.floatToIntBits(value); } + + public boolean equals(Object obj) { + return obj instanceof FloatInfo && ((FloatInfo)obj).value == value; + } + + public int getTag() { return tag; } + + public int copy(ConstPool src, ConstPool dest, Map map) { + return dest.addFloatInfo(value); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeFloat(value); + } + + public void print(PrintWriter out) { + out.print("Float "); + out.println(value); + } +} + +class LongInfo extends ConstInfo { + static final int tag = 5; + long value; + + public LongInfo(long l, int index) { + super(index); + value = l; + } + + public LongInfo(DataInputStream in, int index) throws IOException { + super(index); + value = in.readLong(); + } + + public int hashCode() { return (int)(value ^ (value >>> 32)); } + + public boolean equals(Object obj) { + return obj instanceof LongInfo && ((LongInfo)obj).value == value; + } + + public int getTag() { return tag; } + + public int copy(ConstPool src, ConstPool dest, Map map) { + return dest.addLongInfo(value); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeLong(value); + } + + public void print(PrintWriter out) { + out.print("Long "); + out.println(value); + } +} + +class DoubleInfo extends ConstInfo { + static final int tag = 6; + double value; + + public DoubleInfo(double d, int index) { + super(index); + value = d; + } + + public DoubleInfo(DataInputStream in, int index) throws IOException { + super(index); + value = in.readDouble(); + } + + public int hashCode() { + long v = Double.doubleToLongBits(value); + return (int)(v ^ (v >>> 32)); + } + + public boolean equals(Object obj) { + return obj instanceof DoubleInfo && ((DoubleInfo)obj).value == value; + } + + public int getTag() { return tag; } + + public int copy(ConstPool src, ConstPool dest, Map map) { + return dest.addDoubleInfo(value); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeDouble(value); + } + + public void print(PrintWriter out) { + out.print("Double "); + out.println(value); + } +} + +class Utf8Info extends ConstInfo { + static final int tag = 1; + String string; + + public Utf8Info(String utf8, int index) { + super(index); + string = utf8; + } + + public Utf8Info(DataInputStream in, int index) throws IOException { + super(index); + string = in.readUTF(); + } + + public int hashCode() { + return string.hashCode(); + } + + public boolean equals(Object obj) { + return obj instanceof Utf8Info && ((Utf8Info)obj).string.equals(string); + } + + public int getTag() { return tag; } + + public int copy(ConstPool src, ConstPool dest, Map map) { + return dest.addUtf8Info(string); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeUTF(string); + } + + public void print(PrintWriter out) { + out.print("UTF8 \""); + out.print(string); + out.println("\""); + } +} + +class MethodHandleInfo extends ConstInfo { + static final int tag = 15; + int refKind, refIndex; + + public MethodHandleInfo(int kind, int referenceIndex, int index) { + super(index); + refKind = kind; + refIndex = referenceIndex; + } + + public MethodHandleInfo(DataInputStream in, int index) throws IOException { + super(index); + refKind = in.readUnsignedByte(); + refIndex = in.readUnsignedShort(); + } + + public int hashCode() { return (refKind << 16) ^ refIndex; } + + public boolean equals(Object obj) { + if (obj instanceof MethodHandleInfo) { + MethodHandleInfo mh = (MethodHandleInfo)obj; + return mh.refKind == refKind && mh.refIndex == refIndex; + } + else + return false; + } + + public int getTag() { return tag; } + + public int copy(ConstPool src, ConstPool dest, Map map) { + return dest.addMethodHandleInfo(refKind, + src.getItem(refIndex).copy(src, dest, map)); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeByte(refKind); + out.writeShort(refIndex); + } + + public void print(PrintWriter out) { + out.print("MethodHandle #"); + out.print(refKind); + out.print(", index #"); + out.println(refIndex); + } +} + +class MethodTypeInfo extends ConstInfo { + static final int tag = 16; + int descriptor; + + public MethodTypeInfo(int desc, int index) { + super(index); + descriptor = desc; + } + + public MethodTypeInfo(DataInputStream in, int index) throws IOException { + super(index); + descriptor = in.readUnsignedShort(); + } + + public int hashCode() { return descriptor; } + + public boolean equals(Object obj) { + if (obj instanceof MethodTypeInfo) + return ((MethodTypeInfo)obj).descriptor == descriptor; + else + return false; + } + + public int getTag() { return tag; } + + public void renameClass(ConstPool cp, String oldName, String newName, HashMap cache) { + String desc = cp.getUtf8Info(descriptor); + String desc2 = Descriptor.rename(desc, oldName, newName); + if (desc != desc2) + if (cache == null) + descriptor = cp.addUtf8Info(desc2); + else { + cache.remove(this); + descriptor = cp.addUtf8Info(desc2); + cache.put(this, this); + } + } + + public void renameClass(ConstPool cp, Map map, HashMap cache) { + String desc = cp.getUtf8Info(descriptor); + String desc2 = Descriptor.rename(desc, map); + if (desc != desc2) + if (cache == null) + descriptor = cp.addUtf8Info(desc2); + else { + cache.remove(this); + descriptor = cp.addUtf8Info(desc2); + cache.put(this, this); + } + } + + public int copy(ConstPool src, ConstPool dest, Map map) { + String desc = src.getUtf8Info(descriptor); + desc = Descriptor.rename(desc, map); + return dest.addMethodTypeInfo(dest.addUtf8Info(desc)); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeShort(descriptor); + } + + public void print(PrintWriter out) { + out.print("MethodType #"); + out.println(descriptor); + } +} + +class InvokeDynamicInfo extends ConstInfo { + static final int tag = 18; + int bootstrap, nameAndType; + + public InvokeDynamicInfo(int bootstrapMethod, int ntIndex, int index) { + super(index); + bootstrap = bootstrapMethod; + nameAndType = ntIndex; + } + + public InvokeDynamicInfo(DataInputStream in, int index) throws IOException { + super(index); + bootstrap = in.readUnsignedShort(); + nameAndType = in.readUnsignedShort(); + } + + public int hashCode() { return (bootstrap << 16) ^ nameAndType; } + + public boolean equals(Object obj) { + if (obj instanceof InvokeDynamicInfo) { + InvokeDynamicInfo iv = (InvokeDynamicInfo)obj; + return iv.bootstrap == bootstrap && iv.nameAndType == nameAndType; + } + else + return false; + } + + public int getTag() { return tag; } + + public int copy(ConstPool src, ConstPool dest, Map map) { + return dest.addInvokeDynamicInfo(bootstrap, + src.getItem(nameAndType).copy(src, dest, map)); + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeShort(bootstrap); + out.writeShort(nameAndType); + } + + public void print(PrintWriter out) { + out.print("InvokeDynamic #"); + out.print(bootstrap); + out.print(", name&type #"); + out.println(nameAndType); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java new file mode 100644 index 0000000..900ce1a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java @@ -0,0 +1,73 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.util.Map; +import java.io.IOException; + +/** + * ConstantValue_attribute. + */ +public class ConstantAttribute extends AttributeInfo { + /** + * The name of this attribute "ConstantValue". + */ + public static final String tag = "ConstantValue"; + + ConstantAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Constructs a ConstantValue attribute. + * + * @param cp a constant pool table. + * @param index constantvalue_index + * of ConstantValue_attribute. + */ + public ConstantAttribute(ConstPool cp, int index) { + super(cp, tag); + byte[] bvalue = new byte[2]; + bvalue[0] = (byte)(index >>> 8); + bvalue[1] = (byte)index; + set(bvalue); + } + + /** + * Returns constantvalue_index. + */ + public int getConstantValue() { + return ByteArray.readU16bit(get(), 0); + } + + /** + * Makes a copy. Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + int index = getConstPool().copy(getConstantValue(), newCp, + classnames); + return new ConstantAttribute(newCp, index); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java new file mode 100644 index 0000000..fdf1a7f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java @@ -0,0 +1,56 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +/** + * Deprecated_attribute. + */ +public class DeprecatedAttribute extends AttributeInfo { + /** + * The name of this attribute "Deprecated". + */ + public static final String tag = "Deprecated"; + + DeprecatedAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Constructs a Deprecated attribute. + * + * @param cp a constant pool table. + */ + public DeprecatedAttribute(ConstPool cp) { + super(cp, tag, new byte[0]); + } + + /** + * Makes a copy. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames should be null. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + return new DeprecatedAttribute(newCp); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/Descriptor.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/Descriptor.java new file mode 100644 index 0000000..7b8f2f1 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/Descriptor.java @@ -0,0 +1,872 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtPrimitiveType; +import com.wenshuo.agent.javassist.NotFoundException; +import java.util.Map; + +/** + * A support class for dealing with descriptors. + * + *

See chapter 4.3 in "The Java Virtual Machine Specification (2nd ed.)" + */ +public class Descriptor { + /** + * Converts a class name into the internal representation used in + * the JVM. + * + *

Note that toJvmName(toJvmName(s)) is equivalent + * to toJvmName(s). + */ + public static String toJvmName(String classname) { + return classname.replace('.', '/'); + } + + /** + * Converts a class name from the internal representation used in + * the JVM to the normal one used in Java. + * This method does not deal with an array type name such as + * "[Ljava/lang/Object;" and "[I;". For such names, use + * toClassName(). + * + * @see #toClassName(String) + */ + public static String toJavaName(String classname) { + return classname.replace('/', '.'); + } + + /** + * Returns the internal representation of the class name in the + * JVM. + */ + public static String toJvmName(CtClass clazz) { + if (clazz.isArray()) + return of(clazz); + else + return toJvmName(clazz.getName()); + } + + /** + * Converts to a Java class name from a descriptor. + * + * @param descriptor type descriptor. + */ + public static String toClassName(String descriptor) { + int arrayDim = 0; + int i = 0; + char c = descriptor.charAt(0); + while (c == '[') { + ++arrayDim; + c = descriptor.charAt(++i); + } + + String name; + if (c == 'L') { + int i2 = descriptor.indexOf(';', i++); + name = descriptor.substring(i, i2).replace('/', '.'); + i = i2; + } + else if (c == 'V') + name = "void"; + else if (c == 'I') + name = "int"; + else if (c == 'B') + name = "byte"; + else if (c == 'J') + name = "long"; + else if (c == 'D') + name = "double"; + else if (c == 'F') + name = "float"; + else if (c == 'C') + name = "char"; + else if (c == 'S') + name = "short"; + else if (c == 'Z') + name = "boolean"; + else + throw new RuntimeException("bad descriptor: " + descriptor); + + if (i + 1 != descriptor.length()) + throw new RuntimeException("multiple descriptors?: " + descriptor); + + if (arrayDim == 0) + return name; + else { + StringBuffer sbuf = new StringBuffer(name); + do { + sbuf.append("[]"); + } while (--arrayDim > 0); + + return sbuf.toString(); + } + } + + /** + * Converts to a descriptor from a Java class name + */ + public static String of(String classname) { + if (classname.equals("void")) + return "V"; + else if (classname.equals("int")) + return "I"; + else if (classname.equals("byte")) + return "B"; + else if (classname.equals("long")) + return "J"; + else if (classname.equals("double")) + return "D"; + else if (classname.equals("float")) + return "F"; + else if (classname.equals("char")) + return "C"; + else if (classname.equals("short")) + return "S"; + else if (classname.equals("boolean")) + return "Z"; + else + return "L" + toJvmName(classname) + ";"; + } + + /** + * Substitutes a class name + * in the given descriptor string. + * + * @param desc descriptor string + * @param oldname replaced JVM class name + * @param newname substituted JVM class name + * + * @see Descriptor#toJvmName(String) + */ + public static String rename(String desc, String oldname, String newname) { + if (desc.indexOf(oldname) < 0) + return desc; + + StringBuffer newdesc = new StringBuffer(); + int head = 0; + int i = 0; + for (;;) { + int j = desc.indexOf('L', i); + if (j < 0) + break; + else if (desc.startsWith(oldname, j + 1) + && desc.charAt(j + oldname.length() + 1) == ';') { + newdesc.append(desc.substring(head, j)); + newdesc.append('L'); + newdesc.append(newname); + newdesc.append(';'); + head = i = j + oldname.length() + 2; + } + else { + i = desc.indexOf(';', j) + 1; + if (i < 1) + break; // ';' was not found. + } + } + + if (head == 0) + return desc; + else { + int len = desc.length(); + if (head < len) + newdesc.append(desc.substring(head, len)); + + return newdesc.toString(); + } + } + + /** + * Substitutes class names in the given descriptor string + * according to the given map. + * + * @param map a map between replaced and substituted + * JVM class names. + * @see Descriptor#toJvmName(String) + */ + public static String rename(String desc, Map map) { + if (map == null) + return desc; + + StringBuffer newdesc = new StringBuffer(); + int head = 0; + int i = 0; + for (;;) { + int j = desc.indexOf('L', i); + if (j < 0) + break; + + int k = desc.indexOf(';', j); + if (k < 0) + break; + + i = k + 1; + String name = desc.substring(j + 1, k); + String name2 = (String)map.get(name); + if (name2 != null) { + newdesc.append(desc.substring(head, j)); + newdesc.append('L'); + newdesc.append(name2); + newdesc.append(';'); + head = i; + } + } + + if (head == 0) + return desc; + else { + int len = desc.length(); + if (head < len) + newdesc.append(desc.substring(head, len)); + + return newdesc.toString(); + } + } + + /** + * Returns the descriptor representing the given type. + */ + public static String of(CtClass type) { + StringBuffer sbuf = new StringBuffer(); + toDescriptor(sbuf, type); + return sbuf.toString(); + } + + private static void toDescriptor(StringBuffer desc, CtClass type) { + if (type.isArray()) { + desc.append('['); + try { + toDescriptor(desc, type.getComponentType()); + } + catch (NotFoundException e) { + desc.append('L'); + String name = type.getName(); + desc.append(toJvmName(name.substring(0, name.length() - 2))); + desc.append(';'); + } + } + else if (type.isPrimitive()) { + CtPrimitiveType pt = (CtPrimitiveType)type; + desc.append(pt.getDescriptor()); + } + else { // class type + desc.append('L'); + desc.append(type.getName().replace('.', '/')); + desc.append(';'); + } + } + + /** + * Returns the descriptor representing a constructor receiving + * the given parameter types. + * + * @param paramTypes parameter types + */ + public static String ofConstructor(CtClass[] paramTypes) { + return ofMethod(CtClass.voidType, paramTypes); + } + + /** + * Returns the descriptor representing a method that receives + * the given parameter types and returns the given type. + * + * @param returnType return type + * @param paramTypes parameter types + */ + public static String ofMethod(CtClass returnType, CtClass[] paramTypes) { + StringBuffer desc = new StringBuffer(); + desc.append('('); + if (paramTypes != null) { + int n = paramTypes.length; + for (int i = 0; i < n; ++i) + toDescriptor(desc, paramTypes[i]); + } + + desc.append(')'); + if (returnType != null) + toDescriptor(desc, returnType); + + return desc.toString(); + } + + /** + * Returns the descriptor representing a list of parameter types. + * For example, if the given parameter types are two int, + * then this method returns "(II)". + * + * @param paramTypes parameter types + */ + public static String ofParameters(CtClass[] paramTypes) { + return ofMethod(null, paramTypes); + } + + /** + * Appends a parameter type to the parameter list represented + * by the given descriptor. + * + *

classname must not be an array type. + * + * @param classname parameter type (not primitive type) + * @param desc descriptor + */ + public static String appendParameter(String classname, String desc) { + int i = desc.indexOf(')'); + if (i < 0) + return desc; + else { + StringBuffer newdesc = new StringBuffer(); + newdesc.append(desc.substring(0, i)); + newdesc.append('L'); + newdesc.append(classname.replace('.', '/')); + newdesc.append(';'); + newdesc.append(desc.substring(i)); + return newdesc.toString(); + } + } + + /** + * Inserts a parameter type at the beginning of the parameter + * list represented + * by the given descriptor. + * + *

classname must not be an array type. + * + * @param classname parameter type (not primitive type) + * @param desc descriptor + */ + public static String insertParameter(String classname, String desc) { + if (desc.charAt(0) != '(') + return desc; + else + return "(L" + classname.replace('.', '/') + ';' + + desc.substring(1); + } + + /** + * Appends a parameter type to the parameter list represented + * by the given descriptor. The appended parameter becomes + * the last parameter. + * + * @param type the type of the appended parameter. + * @param descriptor the original descriptor. + */ + public static String appendParameter(CtClass type, String descriptor) { + int i = descriptor.indexOf(')'); + if (i < 0) + return descriptor; + else { + StringBuffer newdesc = new StringBuffer(); + newdesc.append(descriptor.substring(0, i)); + toDescriptor(newdesc, type); + newdesc.append(descriptor.substring(i)); + return newdesc.toString(); + } + } + + /** + * Inserts a parameter type at the beginning of the parameter + * list represented + * by the given descriptor. + * + * @param type the type of the inserted parameter. + * @param descriptor the descriptor of the method. + */ + public static String insertParameter(CtClass type, + String descriptor) { + if (descriptor.charAt(0) != '(') + return descriptor; + else + return "(" + of(type) + descriptor.substring(1); + } + + /** + * Changes the return type included in the given descriptor. + * + *

classname must not be an array type. + * + * @param classname return type + * @param desc descriptor + */ + public static String changeReturnType(String classname, String desc) { + int i = desc.indexOf(')'); + if (i < 0) + return desc; + else { + StringBuffer newdesc = new StringBuffer(); + newdesc.append(desc.substring(0, i + 1)); + newdesc.append('L'); + newdesc.append(classname.replace('.', '/')); + newdesc.append(';'); + return newdesc.toString(); + } + } + + /** + * Returns the CtClass objects representing the parameter + * types specified by the given descriptor. + * + * @param desc descriptor + * @param cp the class pool used for obtaining + * a CtClass object. + */ + public static CtClass[] getParameterTypes(String desc, ClassPool cp) + throws NotFoundException + { + if (desc.charAt(0) != '(') + return null; + else { + int num = numOfParameters(desc); + CtClass[] args = new CtClass[num]; + int n = 0; + int i = 1; + do { + i = toCtClass(cp, desc, i, args, n++); + } while (i > 0); + return args; + } + } + + /** + * Returns true if the list of the parameter types of desc1 is equal to + * that of desc2. + * For example, "(II)V" and "(II)I" are equal. + */ + public static boolean eqParamTypes(String desc1, String desc2) { + if (desc1.charAt(0) != '(') + return false; + + for (int i = 0; true; ++i) { + char c = desc1.charAt(i); + if (c != desc2.charAt(i)) + return false; + + if (c == ')') + return true; + } + } + + /** + * Returns the signature of the given descriptor. The signature does + * not include the return type. For example, the signature of "(I)V" + * is "(I)". + */ + public static String getParamDescriptor(String decl) { + return decl.substring(0, decl.indexOf(')') + 1); + } + + /** + * Returns the CtClass object representing the return + * type specified by the given descriptor. + * + * @param desc descriptor + * @param cp the class pool used for obtaining + * a CtClass object. + */ + public static CtClass getReturnType(String desc, ClassPool cp) + throws NotFoundException + { + int i = desc.indexOf(')'); + if (i < 0) + return null; + else { + CtClass[] type = new CtClass[1]; + toCtClass(cp, desc, i + 1, type, 0); + return type[0]; + } + } + + /** + * Returns the number of the prameters included in the given + * descriptor. + * + * @param desc descriptor + */ + public static int numOfParameters(String desc) { + int n = 0; + int i = 1; + for (;;) { + char c = desc.charAt(i); + if (c == ')') + break; + + while (c == '[') + c = desc.charAt(++i); + + if (c == 'L') { + i = desc.indexOf(';', i) + 1; + if (i <= 0) + throw new IndexOutOfBoundsException("bad descriptor"); + } + else + ++i; + + ++n; + } + + return n; + } + + /** + * Returns a CtClass object representing the type + * specified by the given descriptor. + * + *

This method works even if the package-class separator is + * not / but . (period). For example, + * it accepts Ljava.lang.Object; + * as well as Ljava/lang/Object;. + * + * @param desc descriptor. + * @param cp the class pool used for obtaining + * a CtClass object. + */ + public static CtClass toCtClass(String desc, ClassPool cp) + throws NotFoundException + { + CtClass[] clazz = new CtClass[1]; + int res = toCtClass(cp, desc, 0, clazz, 0); + if (res >= 0) + return clazz[0]; + else { + // maybe, you forgot to surround the class name with + // L and ;. It violates the protocol, but I'm tolerant... + return cp.get(desc.replace('/', '.')); + } + } + + private static int toCtClass(ClassPool cp, String desc, int i, + CtClass[] args, int n) + throws NotFoundException + { + int i2; + String name; + + int arrayDim = 0; + char c = desc.charAt(i); + while (c == '[') { + ++arrayDim; + c = desc.charAt(++i); + } + + if (c == 'L') { + i2 = desc.indexOf(';', ++i); + name = desc.substring(i, i2++).replace('/', '.'); + } + else { + CtClass type = toPrimitiveClass(c); + if (type == null) + return -1; // error + + i2 = i + 1; + if (arrayDim == 0) { + args[n] = type; + return i2; // neither an array type or a class type + } + else + name = type.getName(); + } + + if (arrayDim > 0) { + StringBuffer sbuf = new StringBuffer(name); + while (arrayDim-- > 0) + sbuf.append("[]"); + + name = sbuf.toString(); + } + + args[n] = cp.get(name); + return i2; + } + + static CtClass toPrimitiveClass(char c) { + CtClass type = null; + switch (c) { + case 'Z' : + type = CtClass.booleanType; + break; + case 'C' : + type = CtClass.charType; + break; + case 'B' : + type = CtClass.byteType; + break; + case 'S' : + type = CtClass.shortType; + break; + case 'I' : + type = CtClass.intType; + break; + case 'J' : + type = CtClass.longType; + break; + case 'F' : + type = CtClass.floatType; + break; + case 'D' : + type = CtClass.doubleType; + break; + case 'V' : + type = CtClass.voidType; + break; + } + + return type; + } + + /** + * Computes the dimension of the array represented by the given + * descriptor. For example, if the descriptor is "[[I", + * then this method returns 2. + * + * @param desc the descriptor. + * @return 0 if the descriptor does not represent an array type. + */ + public static int arrayDimension(String desc) { + int dim = 0; + while (desc.charAt(dim) == '[') + ++dim; + + return dim; + } + + /** + * Returns the descriptor of the type of the array component. + * For example, if the given descriptor is + * "[[Ljava/lang/String;" and the given dimension is 2, + * then this method returns "Ljava/lang/String;". + * + * @param desc the descriptor. + * @param dim the array dimension. + */ + public static String toArrayComponent(String desc, int dim) { + return desc.substring(dim); + } + + /** + * Computes the data size specified by the given descriptor. + * For example, if the descriptor is "D", this method returns 2. + * + *

If the descriptor represents a method type, this method returns + * (the size of the returned value) - (the sum of the data sizes + * of all the parameters). For example, if the descriptor is + * "(I)D", then this method returns 1 (= 2 - 1). + * + * @param desc descriptor + */ + public static int dataSize(String desc) { + return dataSize(desc, true); + } + + /** + * Computes the data size of parameters. + * If one of the parameters is double type, the size of that parameter + * is 2 words. For example, if the given descriptor is + * "(IJ)D", then this method returns 3. The size of the + * return type is not computed. + * + * @param desc a method descriptor. + */ + public static int paramSize(String desc) { + return -dataSize(desc, false); + } + + private static int dataSize(String desc, boolean withRet) { + int n = 0; + char c = desc.charAt(0); + if (c == '(') { + int i = 1; + for (;;) { + c = desc.charAt(i); + if (c == ')') { + c = desc.charAt(i + 1); + break; + } + + boolean array = false; + while (c == '[') { + array = true; + c = desc.charAt(++i); + } + + if (c == 'L') { + i = desc.indexOf(';', i) + 1; + if (i <= 0) + throw new IndexOutOfBoundsException("bad descriptor"); + } + else + ++i; + + if (!array && (c == 'J' || c == 'D')) + n -= 2; + else + --n; + } + } + + if (withRet) + if (c == 'J' || c == 'D') + n += 2; + else if (c != 'V') + ++n; + + return n; + } + + /** + * Returns a human-readable representation of the + * given descriptor. For example, Ljava/lang/Object; + * is converted into java.lang.Object. + * (I[I)V is converted into (int, int[]) + * (the return type is ignored). + */ + public static String toString(String desc) { + return PrettyPrinter.toString(desc); + } + + static class PrettyPrinter { + static String toString(String desc) { + StringBuffer sbuf = new StringBuffer(); + if (desc.charAt(0) == '(') { + int pos = 1; + sbuf.append('('); + while (desc.charAt(pos) != ')') { + if (pos > 1) + sbuf.append(','); + + pos = readType(sbuf, pos, desc); + } + + sbuf.append(')'); + } + else + readType(sbuf, 0, desc); + + return sbuf.toString(); + } + + static int readType(StringBuffer sbuf, int pos, String desc) { + char c = desc.charAt(pos); + int arrayDim = 0; + while (c == '[') { + arrayDim++; + c = desc.charAt(++pos); + } + + if (c == 'L') + while (true) { + c = desc.charAt(++pos); + if (c == ';') + break; + + if (c == '/') + c = '.'; + + sbuf.append(c); + } + else { + CtClass t = toPrimitiveClass(c); + sbuf.append(t.getName()); + } + + while (arrayDim-- > 0) + sbuf.append("[]"); + + return pos + 1; + } + } + + /** + * An Iterator over a descriptor. + */ + public static class Iterator { + private String desc; + private int index, curPos; + private boolean param; + + /** + * Constructs an iterator. + * + * @param s descriptor. + */ + public Iterator(String s) { + desc = s; + index = curPos = 0; + param = false; + } + + /** + * Returns true if the iteration has more elements. + */ + public boolean hasNext() { + return index < desc.length(); + } + + /** + * Returns true if the current element is a parameter type. + */ + public boolean isParameter() { return param; } + + /** + * Returns the first character of the current element. + */ + public char currentChar() { return desc.charAt(curPos); } + + /** + * Returns true if the current element is double or long type. + */ + public boolean is2byte() { + char c = currentChar(); + return c == 'D' || c == 'J'; + } + + /** + * Returns the position of the next type character. + * That type character becomes a new current element. + */ + public int next() { + int nextPos = index; + char c = desc.charAt(nextPos); + if (c == '(') { + ++index; + c = desc.charAt(++nextPos); + param = true; + } + + if (c == ')') { + ++index; + c = desc.charAt(++nextPos); + param = false; + } + + while (c == '[') + c = desc.charAt(++nextPos); + + if (c == 'L') { + nextPos = desc.indexOf(';', nextPos) + 1; + if (nextPos <= 0) + throw new IndexOutOfBoundsException("bad descriptor"); + } + else + ++nextPos; + + curPos = index; + index = nextPos; + return curPos; + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java new file mode 100644 index 0000000..1035450 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java @@ -0,0 +1,31 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import com.wenshuo.agent.javassist.CannotCompileException; + +/** + * An exception thrown when adding a duplicate member is requested. + * + * @see ClassFile#addMethod(MethodInfo) + * @see ClassFile#addField(FieldInfo) + */ +public class DuplicateMemberException extends CannotCompileException { + public DuplicateMemberException(String msg) { + super(msg); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java new file mode 100644 index 0000000..05a0f4a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java @@ -0,0 +1,142 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +import com.wenshuo.agent.javassist.CtConstructor; + +/** + * EnclosingMethod_attribute. + */ +public class EnclosingMethodAttribute extends AttributeInfo { + /** + * The name of this attribute "EnclosingMethod". + */ + public static final String tag = "EnclosingMethod"; + + EnclosingMethodAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Constructs an EnclosingMethod attribute. + * + * @param cp a constant pool table. + * @param className the name of the innermost enclosing class. + * @param methodName the name of the enclosing method. + * @param methodDesc the descriptor of the enclosing method. + */ + public EnclosingMethodAttribute(ConstPool cp, String className, + String methodName, String methodDesc) { + super(cp, tag); + int ci = cp.addClassInfo(className); + int ni = cp.addNameAndTypeInfo(methodName, methodDesc); + byte[] bvalue = new byte[4]; + bvalue[0] = (byte)(ci >>> 8); + bvalue[1] = (byte)ci; + bvalue[2] = (byte)(ni >>> 8); + bvalue[3] = (byte)ni; + set(bvalue); + } + + /** + * Constructs an EnclosingMethod attribute. + * The value of method_index is set to 0. + * + * @param cp a constant pool table. + * @param className the name of the innermost enclosing class. + */ + public EnclosingMethodAttribute(ConstPool cp, String className) { + super(cp, tag); + int ci = cp.addClassInfo(className); + int ni = 0; + byte[] bvalue = new byte[4]; + bvalue[0] = (byte)(ci >>> 8); + bvalue[1] = (byte)ci; + bvalue[2] = (byte)(ni >>> 8); + bvalue[3] = (byte)ni; + set(bvalue); + } + + /** + * Returns the value of class_index. + */ + public int classIndex() { + return ByteArray.readU16bit(get(), 0); + } + + /** + * Returns the value of method_index. + */ + public int methodIndex() { + return ByteArray.readU16bit(get(), 2); + } + + /** + * Returns the name of the class specified by class_index. + */ + public String className() { + return getConstPool().getClassInfo(classIndex()); + } + + /** + * Returns the method name specified by method_index. + * If the method is a class initializer (static constructor), + * {@link MethodInfo#nameClinit} is returned. + */ + public String methodName() { + ConstPool cp = getConstPool(); + int mi = methodIndex(); + if (mi == 0) + return MethodInfo.nameClinit; + else { + int ni = cp.getNameAndTypeName(mi); + return cp.getUtf8Info(ni); + } + } + + /** + * Returns the method descriptor specified by method_index. + */ + public String methodDescriptor() { + ConstPool cp = getConstPool(); + int mi = methodIndex(); + int ti = cp.getNameAndTypeDescriptor(mi); + return cp.getUtf8Info(ti); + } + + /** + * Makes a copy. Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + if (methodIndex() == 0) + return new EnclosingMethodAttribute(newCp, className()); + else + return new EnclosingMethodAttribute(newCp, className(), + methodName(), methodDescriptor()); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java new file mode 100644 index 0000000..9ff2fa5 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java @@ -0,0 +1,281 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Map; + +class ExceptionTableEntry { + int startPc; + int endPc; + int handlerPc; + int catchType; + + ExceptionTableEntry(int start, int end, int handle, int type) { + startPc = start; + endPc = end; + handlerPc = handle; + catchType = type; + } +} + +/** + * exception_table[] of Code_attribute. + */ +public class ExceptionTable implements Cloneable { + private ConstPool constPool; + private ArrayList entries; + + /** + * Constructs an exception_table[]. + * + * @param cp constant pool table. + */ + public ExceptionTable(ConstPool cp) { + constPool = cp; + entries = new ArrayList(); + } + + ExceptionTable(ConstPool cp, DataInputStream in) throws IOException { + constPool = cp; + int length = in.readUnsignedShort(); + ArrayList list = new ArrayList(length); + for (int i = 0; i < length; ++i) { + int start = in.readUnsignedShort(); + int end = in.readUnsignedShort(); + int handle = in.readUnsignedShort(); + int type = in.readUnsignedShort(); + list.add(new ExceptionTableEntry(start, end, handle, type)); + } + + entries = list; + } + + /** + * Creates and returns a copy of this object. + * The constant pool object is shared between this object + * and the cloned object. + */ + public Object clone() throws CloneNotSupportedException { + ExceptionTable r = (ExceptionTable)super.clone(); + r.entries = new ArrayList(entries); + return r; + } + + /** + * Returns exception_table_length, which is the number + * of entries in the exception_table[]. + */ + public int size() { + return entries.size(); + } + + /** + * Returns startPc of the n-th entry. + * + * @param nth the n-th (>= 0). + */ + public int startPc(int nth) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); + return e.startPc; + } + + /** + * Sets startPc of the n-th entry. + * + * @param nth the n-th (>= 0). + * @param value new value. + */ + public void setStartPc(int nth, int value) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); + e.startPc = value; + } + + /** + * Returns endPc of the n-th entry. + * + * @param nth the n-th (>= 0). + */ + public int endPc(int nth) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); + return e.endPc; + } + + /** + * Sets endPc of the n-th entry. + * + * @param nth the n-th (>= 0). + * @param value new value. + */ + public void setEndPc(int nth, int value) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); + e.endPc = value; + } + + /** + * Returns handlerPc of the n-th entry. + * + * @param nth the n-th (>= 0). + */ + public int handlerPc(int nth) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); + return e.handlerPc; + } + + /** + * Sets handlerPc of the n-th entry. + * + * @param nth the n-th (>= 0). + * @param value new value. + */ + public void setHandlerPc(int nth, int value) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); + e.handlerPc = value; + } + + /** + * Returns catchType of the n-th entry. + * + * @param nth the n-th (>= 0). + * @return an index into the constant_pool table, + * or zero if this exception handler is for all exceptions. + */ + public int catchType(int nth) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); + return e.catchType; + } + + /** + * Sets catchType of the n-th entry. + * + * @param nth the n-th (>= 0). + * @param value new value. + */ + public void setCatchType(int nth, int value) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); + e.catchType = value; + } + + /** + * Copies the given exception table at the specified position + * in the table. + * + * @param index index (>= 0) at which the entry is to be inserted. + * @param offset the offset added to the code position. + */ + public void add(int index, ExceptionTable table, int offset) { + int len = table.size(); + while (--len >= 0) { + ExceptionTableEntry e + = (ExceptionTableEntry)table.entries.get(len); + add(index, e.startPc + offset, e.endPc + offset, + e.handlerPc + offset, e.catchType); + } + } + + /** + * Adds a new entry at the specified position in the table. + * + * @param index index (>= 0) at which the entry is to be inserted. + * @param start startPc + * @param end endPc + * @param handler handlerPc + * @param type catchType + */ + public void add(int index, int start, int end, int handler, int type) { + if (start < end) + entries.add(index, + new ExceptionTableEntry(start, end, handler, type)); + } + + /** + * Appends a new entry at the end of the table. + * + * @param start startPc + * @param end endPc + * @param handler handlerPc + * @param type catchType + */ + public void add(int start, int end, int handler, int type) { + if (start < end) + entries.add(new ExceptionTableEntry(start, end, handler, type)); + } + + /** + * Removes the entry at the specified position in the table. + * + * @param index the index of the removed entry. + */ + public void remove(int index) { + entries.remove(index); + } + + /** + * Makes a copy of this exception_table[]. + * Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. + */ + public ExceptionTable copy(ConstPool newCp, Map classnames) { + ExceptionTable et = new ExceptionTable(newCp); + ConstPool srcCp = constPool; + int len = size(); + for (int i = 0; i < len; ++i) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i); + int type = srcCp.copy(e.catchType, newCp, classnames); + et.add(e.startPc, e.endPc, e.handlerPc, type); + } + + return et; + } + + void shiftPc(int where, int gapLength, boolean exclusive) { + int len = size(); + for (int i = 0; i < len; ++i) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i); + e.startPc = shiftPc(e.startPc, where, gapLength, exclusive); + e.endPc = shiftPc(e.endPc, where, gapLength, exclusive); + e.handlerPc = shiftPc(e.handlerPc, where, gapLength, exclusive); + } + } + + private static int shiftPc(int pc, int where, int gapLength, + boolean exclusive) { + if (pc > where || (exclusive && pc == where)) + pc += gapLength; + + return pc; + } + + void write(DataOutputStream out) throws IOException { + int len = size(); + out.writeShort(len); // exception_table_length + for (int i = 0; i < len; ++i) { + ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i); + out.writeShort(e.startPc); + out.writeShort(e.endPc); + out.writeShort(e.handlerPc); + out.writeShort(e.catchType); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java new file mode 100644 index 0000000..e20a621 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java @@ -0,0 +1,174 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +/** + * Exceptions_attribute. + */ +public class ExceptionsAttribute extends AttributeInfo { + /** + * The name of this attribute "Exceptions". + */ + public static final String tag = "Exceptions"; + + ExceptionsAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Constructs a copy of an exceptions attribute. + * + * @param cp constant pool table. + * @param src source attribute. + */ + private ExceptionsAttribute(ConstPool cp, ExceptionsAttribute src, + Map classnames) { + super(cp, tag); + copyFrom(src, classnames); + } + + /** + * Constructs a new exceptions attribute. + * + * @param cp constant pool table. + */ + public ExceptionsAttribute(ConstPool cp) { + super(cp, tag); + byte[] data = new byte[2]; + data[0] = data[1] = 0; // empty + this.info = data; + } + + /** + * Makes a copy. Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. It can be null. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + return new ExceptionsAttribute(newCp, this, classnames); + } + + /** + * Copies the contents from a source attribute. + * Specified class names are replaced during the copy. + * + * @param srcAttr source Exceptions attribute + * @param classnames pairs of replaced and substituted + * class names. + */ + private void copyFrom(ExceptionsAttribute srcAttr, Map classnames) { + ConstPool srcCp = srcAttr.constPool; + ConstPool destCp = this.constPool; + byte[] src = srcAttr.info; + int num = src.length; + byte[] dest = new byte[num]; + dest[0] = src[0]; + dest[1] = src[1]; // the number of elements. + for (int i = 2; i < num; i += 2) { + int index = ByteArray.readU16bit(src, i); + ByteArray.write16bit(srcCp.copy(index, destCp, classnames), + dest, i); + } + + this.info = dest; + } + + /** + * Returns exception_index_table[]. + */ + public int[] getExceptionIndexes() { + byte[] blist = info; + int n = blist.length; + if (n <= 2) + return null; + + int[] elist = new int[n / 2 - 1]; + int k = 0; + for (int j = 2; j < n; j += 2) + elist[k++] = ((blist[j] & 0xff) << 8) | (blist[j + 1] & 0xff); + + return elist; + } + + /** + * Returns the names of exceptions that the method may throw. + */ + public String[] getExceptions() { + byte[] blist = info; + int n = blist.length; + if (n <= 2) + return null; + + String[] elist = new String[n / 2 - 1]; + int k = 0; + for (int j = 2; j < n; j += 2) { + int index = ((blist[j] & 0xff) << 8) | (blist[j + 1] & 0xff); + elist[k++] = constPool.getClassInfo(index); + } + + return elist; + } + + /** + * Sets exception_index_table[]. + */ + public void setExceptionIndexes(int[] elist) { + int n = elist.length; + byte[] blist = new byte[n * 2 + 2]; + ByteArray.write16bit(n, blist, 0); + for (int i = 0; i < n; ++i) + ByteArray.write16bit(elist[i], blist, i * 2 + 2); + + info = blist; + } + + /** + * Sets the names of exceptions that the method may throw. + */ + public void setExceptions(String[] elist) { + int n = elist.length; + byte[] blist = new byte[n * 2 + 2]; + ByteArray.write16bit(n, blist, 0); + for (int i = 0; i < n; ++i) + ByteArray.write16bit(constPool.addClassInfo(elist[i]), + blist, i * 2 + 2); + + info = blist; + } + + /** + * Returns number_of_exceptions. + */ + public int tableLength() { return info.length / 2 - 1; } + + /** + * Returns the value of exception_index_table[nth]. + */ + public int getException(int nth) { + int index = nth * 2 + 2; // nth >= 0 + return ((info[index] & 0xff) << 8) | (info[index + 1] & 0xff); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/FieldInfo.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/FieldInfo.java new file mode 100644 index 0000000..62dd9fa --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/FieldInfo.java @@ -0,0 +1,276 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; + +/** + * field_info structure. + * + *

The following code adds a public field width + * of int type: + *

+ * ClassFile cf = ...
+ * FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I");
+ * f.setAccessFlags(AccessFlag.PUBLIC);
+ * cf.addField(f);
+ * 
+ * + * @see javassist.CtField#getFieldInfo() + */ +public final class FieldInfo { + ConstPool constPool; + int accessFlags; + int name; + String cachedName; + String cachedType; + int descriptor; + ArrayList attribute; // may be null. + + private FieldInfo(ConstPool cp) { + constPool = cp; + accessFlags = 0; + attribute = null; + } + + /** + * Constructs a field_info structure. + * + * @param cp a constant pool table + * @param fieldName field name + * @param desc field descriptor + * + * @see Descriptor + */ + public FieldInfo(ConstPool cp, String fieldName, String desc) { + this(cp); + name = cp.addUtf8Info(fieldName); + cachedName = fieldName; + descriptor = cp.addUtf8Info(desc); + } + + FieldInfo(ConstPool cp, DataInputStream in) throws IOException { + this(cp); + read(in); + } + + /** + * Returns a string representation of the object. + */ + public String toString() { + return getName() + " " + getDescriptor(); + } + + /** + * Copies all constant pool items to a given new constant pool + * and replaces the original items with the new ones. + * This is used for garbage collecting the items of removed fields + * and methods. + * + * @param cp the destination + */ + void compact(ConstPool cp) { + name = cp.addUtf8Info(getName()); + descriptor = cp.addUtf8Info(getDescriptor()); + attribute = AttributeInfo.copyAll(attribute, cp); + constPool = cp; + } + + void prune(ConstPool cp) { + ArrayList newAttributes = new ArrayList(); + AttributeInfo invisibleAnnotations + = getAttribute(AnnotationsAttribute.invisibleTag); + if (invisibleAnnotations != null) { + invisibleAnnotations = invisibleAnnotations.copy(cp, null); + newAttributes.add(invisibleAnnotations); + } + + AttributeInfo visibleAnnotations + = getAttribute(AnnotationsAttribute.visibleTag); + if (visibleAnnotations != null) { + visibleAnnotations = visibleAnnotations.copy(cp, null); + newAttributes.add(visibleAnnotations); + } + + AttributeInfo signature + = getAttribute(SignatureAttribute.tag); + if (signature != null) { + signature = signature.copy(cp, null); + newAttributes.add(signature); + } + + int index = getConstantValue(); + if (index != 0) { + index = constPool.copy(index, cp, null); + newAttributes.add(new ConstantAttribute(cp, index)); + } + + attribute = newAttributes; + name = cp.addUtf8Info(getName()); + descriptor = cp.addUtf8Info(getDescriptor()); + constPool = cp; + } + + /** + * Returns the constant pool table used + * by this field_info. + */ + public ConstPool getConstPool() { + return constPool; + } + + /** + * Returns the field name. + */ + public String getName() { + if (cachedName == null) + cachedName = constPool.getUtf8Info(name); + + return cachedName; + } + + /** + * Sets the field name. + */ + public void setName(String newName) { + name = constPool.addUtf8Info(newName); + cachedName = newName; + } + + /** + * Returns the access flags. + * + * @see AccessFlag + */ + public int getAccessFlags() { + return accessFlags; + } + + /** + * Sets the access flags. + * + * @see AccessFlag + */ + public void setAccessFlags(int acc) { + accessFlags = acc; + } + + /** + * Returns the field descriptor. + * + * @see Descriptor + */ + public String getDescriptor() { + return constPool.getUtf8Info(descriptor); + } + + /** + * Sets the field descriptor. + * + * @see Descriptor + */ + public void setDescriptor(String desc) { + if (!desc.equals(getDescriptor())) + descriptor = constPool.addUtf8Info(desc); + } + + /** + * Finds a ConstantValue attribute and returns the index into + * the constant_pool table. + * + * @return 0 if a ConstantValue attribute is not found. + */ + public int getConstantValue() { + if ((accessFlags & AccessFlag.STATIC) == 0) + return 0; + + ConstantAttribute attr + = (ConstantAttribute)getAttribute(ConstantAttribute.tag); + if (attr == null) + return 0; + else + return attr.getConstantValue(); + } + + /** + * Returns all the attributes. The returned List object + * is shared with this object. If you add a new attribute to the list, + * the attribute is also added to the field represented by this + * object. If you remove an attribute from the list, it is also removed + * from the field. + * + * @return a list of AttributeInfo objects. + * @see AttributeInfo + */ + public List getAttributes() { + if (attribute == null) + attribute = new ArrayList(); + + return attribute; + } + + /** + * Returns the attribute with the specified name. + * It returns null if the specified attribute is not found. + * + * @param name attribute name + * @see #getAttributes() + */ + public AttributeInfo getAttribute(String name) { + return AttributeInfo.lookup(attribute, name); + } + + /** + * Appends an attribute. If there is already an attribute with + * the same name, the new one substitutes for it. + * + * @see #getAttributes() + */ + public void addAttribute(AttributeInfo info) { + if (attribute == null) + attribute = new ArrayList(); + + AttributeInfo.remove(attribute, info.getName()); + attribute.add(info); + } + + private void read(DataInputStream in) throws IOException { + accessFlags = in.readUnsignedShort(); + name = in.readUnsignedShort(); + descriptor = in.readUnsignedShort(); + int n = in.readUnsignedShort(); + attribute = new ArrayList(); + for (int i = 0; i < n; ++i) + attribute.add(AttributeInfo.read(constPool, in)); + } + + void write(DataOutputStream out) throws IOException { + out.writeShort(accessFlags); + out.writeShort(name); + out.writeShort(descriptor); + if (attribute == null) + out.writeShort(0); + else { + out.writeShort(attribute.size()); + AttributeInfo.writeAll(attribute, out); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java new file mode 100644 index 0000000..895c320 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java @@ -0,0 +1,242 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.util.Map; +import java.io.IOException; + +/** + * InnerClasses_attribute. + */ +public class InnerClassesAttribute extends AttributeInfo { + /** + * The name of this attribute "InnerClasses". + */ + public static final String tag = "InnerClasses"; + + InnerClassesAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + private InnerClassesAttribute(ConstPool cp, byte[] info) { + super(cp, tag, info); + } + + /** + * Constructs an empty InnerClasses attribute. + * + * @see #append(String, String, String, int) + */ + public InnerClassesAttribute(ConstPool cp) { + super(cp, tag, new byte[2]); + ByteArray.write16bit(0, get(), 0); + } + + /** + * Returns number_of_classes. + */ + public int tableLength() { return ByteArray.readU16bit(get(), 0); } + + /** + * Returns classes[nth].inner_class_info_index. + */ + public int innerClassIndex(int nth) { + return ByteArray.readU16bit(get(), nth * 8 + 2); + } + + /** + * Returns the class name indicated + * by classes[nth].inner_class_info_index. + * + * @return null or the class name. + */ + public String innerClass(int nth) { + int i = innerClassIndex(nth); + if (i == 0) + return null; + else + return constPool.getClassInfo(i); + } + + /** + * Sets classes[nth].inner_class_info_index to + * the given index. + */ + public void setInnerClassIndex(int nth, int index) { + ByteArray.write16bit(index, get(), nth * 8 + 2); + } + + /** + * Returns classes[nth].outer_class_info_index. + */ + public int outerClassIndex(int nth) { + return ByteArray.readU16bit(get(), nth * 8 + 4); + } + + /** + * Returns the class name indicated + * by classes[nth].outer_class_info_index. + * + * @return null or the class name. + */ + public String outerClass(int nth) { + int i = outerClassIndex(nth); + if (i == 0) + return null; + else + return constPool.getClassInfo(i); + } + + /** + * Sets classes[nth].outer_class_info_index to + * the given index. + */ + public void setOuterClassIndex(int nth, int index) { + ByteArray.write16bit(index, get(), nth * 8 + 4); + } + + /** + * Returns classes[nth].inner_name_index. + */ + public int innerNameIndex(int nth) { + return ByteArray.readU16bit(get(), nth * 8 + 6); + } + + /** + * Returns the simple class name indicated + * by classes[nth].inner_name_index. + * + * @return null or the class name. + */ + public String innerName(int nth) { + int i = innerNameIndex(nth); + if (i == 0) + return null; + else + return constPool.getUtf8Info(i); + } + + /** + * Sets classes[nth].inner_name_index to + * the given index. + */ + public void setInnerNameIndex(int nth, int index) { + ByteArray.write16bit(index, get(), nth * 8 + 6); + } + + /** + * Returns classes[nth].inner_class_access_flags. + */ + public int accessFlags(int nth) { + return ByteArray.readU16bit(get(), nth * 8 + 8); + } + + /** + * Sets classes[nth].inner_class_access_flags to + * the given index. + */ + public void setAccessFlags(int nth, int flags) { + ByteArray.write16bit(flags, get(), nth * 8 + 8); + } + + /** + * Appends a new entry. + * + * @param inner inner_class_info_index + * @param outer outer_class_info_index + * @param name inner_name_index + * @param flags inner_class_access_flags + */ + public void append(String inner, String outer, String name, int flags) { + int i = constPool.addClassInfo(inner); + int o = constPool.addClassInfo(outer); + int n = constPool.addUtf8Info(name); + append(i, o, n, flags); + } + + /** + * Appends a new entry. + * + * @param inner inner_class_info_index + * @param outer outer_class_info_index + * @param name inner_name_index + * @param flags inner_class_access_flags + */ + public void append(int inner, int outer, int name, int flags) { + byte[] data = get(); + int len = data.length; + byte[] newData = new byte[len + 8]; + for (int i = 2; i < len; ++i) + newData[i] = data[i]; + + int n = ByteArray.readU16bit(data, 0); + ByteArray.write16bit(n + 1, newData, 0); + + ByteArray.write16bit(inner, newData, len); + ByteArray.write16bit(outer, newData, len + 2); + ByteArray.write16bit(name, newData, len + 4); + ByteArray.write16bit(flags, newData, len + 6); + + set(newData); + } + + /** + * Makes a copy. Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + byte[] src = get(); + byte[] dest = new byte[src.length]; + ConstPool cp = getConstPool(); + InnerClassesAttribute attr = new InnerClassesAttribute(newCp, dest); + int n = ByteArray.readU16bit(src, 0); + ByteArray.write16bit(n, dest, 0); + int j = 2; + for (int i = 0; i < n; ++i) { + int innerClass = ByteArray.readU16bit(src, j); + int outerClass = ByteArray.readU16bit(src, j + 2); + int innerName = ByteArray.readU16bit(src, j + 4); + int innerAccess = ByteArray.readU16bit(src, j + 6); + + if (innerClass != 0) + innerClass = cp.copy(innerClass, newCp, classnames); + + ByteArray.write16bit(innerClass, dest, j); + + if (outerClass != 0) + outerClass = cp.copy(outerClass, newCp, classnames); + + ByteArray.write16bit(outerClass, dest, j + 2); + + if (innerName != 0) + innerName = cp.copy(innerName, newCp, classnames); + + ByteArray.write16bit(innerName, dest, j + 4); + ByteArray.write16bit(innerAccess, dest, j + 6); + j += 8; + } + + return attr; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java new file mode 100644 index 0000000..c9cc815 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java @@ -0,0 +1,295 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode; + +import java.io.PrintStream; + +import com.wenshuo.agent.javassist.CtMethod; + +/** + * Simple utility class for printing the bytecode instructions of a method. + * + * @author Jason T. Greene + */ +public class InstructionPrinter implements Opcode { + + private final static String opcodes[] = Mnemonic.OPCODE; + private final PrintStream stream; + + /** + * Constructs a InstructionPrinter object. + */ + public InstructionPrinter(PrintStream stream) { + this.stream = stream; + } + + /** + * Prints the bytecode instructions of a given method. + */ + public static void print(CtMethod method, PrintStream stream) { + (new InstructionPrinter(stream)).print(method); + } + + /** + * Prints the bytecode instructions of a given method. + */ + public void print(CtMethod method) { + MethodInfo info = method.getMethodInfo2(); + ConstPool pool = info.getConstPool(); + CodeAttribute code = info.getCodeAttribute(); + if (code == null) + return; + + CodeIterator iterator = code.iterator(); + while (iterator.hasNext()) { + int pos; + try { + pos = iterator.next(); + } catch (BadBytecode e) { + throw new RuntimeException(e); + } + + stream.println(pos + ": " + instructionString(iterator, pos, pool)); + } + } + + /** + * Gets a string representation of the bytecode instruction at the specified + * position. + */ + public static String instructionString(CodeIterator iter, int pos, ConstPool pool) { + int opcode = iter.byteAt(pos); + + if (opcode > opcodes.length || opcode < 0) + throw new IllegalArgumentException("Invalid opcode, opcode: " + opcode + " pos: "+ pos); + + String opstring = opcodes[opcode]; + switch (opcode) { + case BIPUSH: + return opstring + " " + iter.byteAt(pos + 1); + case SIPUSH: + return opstring + " " + iter.s16bitAt(pos + 1); + case LDC: + return opstring + " " + ldc(pool, iter.byteAt(pos + 1)); + case LDC_W : + case LDC2_W : + return opstring + " " + ldc(pool, iter.u16bitAt(pos + 1)); + case ILOAD: + case LLOAD: + case FLOAD: + case DLOAD: + case ALOAD: + case ISTORE: + case LSTORE: + case FSTORE: + case DSTORE: + case ASTORE: + return opstring + " " + iter.byteAt(pos + 1); + case IFEQ: + case IFGE: + case IFGT: + case IFLE: + case IFLT: + case IFNE: + case IFNONNULL: + case IFNULL: + case IF_ACMPEQ: + case IF_ACMPNE: + case IF_ICMPEQ: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + case IF_ICMPLT: + case IF_ICMPNE: + return opstring + " " + (iter.s16bitAt(pos + 1) + pos); + case IINC: + return opstring + " " + iter.byteAt(pos + 1) + ", " + iter.signedByteAt(pos + 2); + case GOTO: + case JSR: + return opstring + " " + (iter.s16bitAt(pos + 1) + pos); + case RET: + return opstring + " " + iter.byteAt(pos + 1); + case TABLESWITCH: + return tableSwitch(iter, pos); + case LOOKUPSWITCH: + return lookupSwitch(iter, pos); + case GETSTATIC: + case PUTSTATIC: + case GETFIELD: + case PUTFIELD: + return opstring + " " + fieldInfo(pool, iter.u16bitAt(pos + 1)); + case INVOKEVIRTUAL: + case INVOKESPECIAL: + case INVOKESTATIC: + return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1)); + case INVOKEINTERFACE: + return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1)); + case INVOKEDYNAMIC: + return opstring + " " + iter.u16bitAt(pos + 1); + case NEW: + return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); + case NEWARRAY: + return opstring + " " + arrayInfo(iter.byteAt(pos + 1)); + case ANEWARRAY: + case CHECKCAST: + return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); + case WIDE: + return wide(iter, pos); + case MULTIANEWARRAY: + return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); + case GOTO_W: + case JSR_W: + return opstring + " " + (iter.s32bitAt(pos + 1)+ pos); + default: + return opstring; + } + } + + + private static String wide(CodeIterator iter, int pos) { + int opcode = iter.byteAt(pos + 1); + int index = iter.u16bitAt(pos + 2); + switch (opcode) { + case ILOAD: + case LLOAD: + case FLOAD: + case DLOAD: + case ALOAD: + case ISTORE: + case LSTORE: + case FSTORE: + case DSTORE: + case ASTORE: + case IINC: + case RET: + return opcodes[opcode] + " " + index; + default: + throw new RuntimeException("Invalid WIDE operand"); + } + } + + + private static String arrayInfo(int type) { + switch (type) { + case T_BOOLEAN: + return "boolean"; + case T_CHAR: + return "char"; + case T_BYTE: + return "byte"; + case T_SHORT: + return "short"; + case T_INT: + return "int"; + case T_LONG: + return "long"; + case T_FLOAT: + return "float"; + case T_DOUBLE: + return "double"; + default: + throw new RuntimeException("Invalid array type"); + } + } + + + private static String classInfo(ConstPool pool, int index) { + return "#" + index + " = Class " + pool.getClassInfo(index); + } + + + private static String interfaceMethodInfo(ConstPool pool, int index) { + return "#" + index + " = Method " + + pool.getInterfaceMethodrefClassName(index) + "." + + pool.getInterfaceMethodrefName(index) + "(" + + pool.getInterfaceMethodrefType(index) + ")"; + } + + private static String methodInfo(ConstPool pool, int index) { + return "#" + index + " = Method " + + pool.getMethodrefClassName(index) + "." + + pool.getMethodrefName(index) + "(" + + pool.getMethodrefType(index) + ")"; + } + + + private static String fieldInfo(ConstPool pool, int index) { + return "#" + index + " = Field " + + pool.getFieldrefClassName(index) + "." + + pool.getFieldrefName(index) + "(" + + pool.getFieldrefType(index) + ")"; + } + + + private static String lookupSwitch(CodeIterator iter, int pos) { + StringBuffer buffer = new StringBuffer("lookupswitch {\n"); + int index = (pos & ~3) + 4; + // default + buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); + int npairs = iter.s32bitAt(index += 4); + int end = npairs * 8 + (index += 4); + + for (; index < end; index += 8) { + int match = iter.s32bitAt(index); + int target = iter.s32bitAt(index + 4) + pos; + buffer.append("\t\t").append(match).append(": ").append(target).append("\n"); + } + + buffer.setCharAt(buffer.length() - 1, '}'); + return buffer.toString(); + } + + + private static String tableSwitch(CodeIterator iter, int pos) { + StringBuffer buffer = new StringBuffer("tableswitch {\n"); + int index = (pos & ~3) + 4; + // default + buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); + int low = iter.s32bitAt(index += 4); + int high = iter.s32bitAt(index += 4); + int end = (high - low + 1) * 4 + (index += 4); + + // Offset table + for (int key = low; index < end; index += 4, key++) { + int target = iter.s32bitAt(index) + pos; + buffer.append("\t\t").append(key).append(": ").append(target).append("\n"); + } + + buffer.setCharAt(buffer.length() - 1, '}'); + return buffer.toString(); + } + + + private static String ldc(ConstPool pool, int index) { + int tag = pool.getTag(index); + switch (tag) { + case ConstPool.CONST_String: + return "#" + index + " = \"" + pool.getStringInfo(index) + "\""; + case ConstPool.CONST_Integer: + return "#" + index + " = int " + pool.getIntegerInfo(index); + case ConstPool.CONST_Float: + return "#" + index + " = float " + pool.getFloatInfo(index); + case ConstPool.CONST_Long: + return "#" + index + " = long " + pool.getLongInfo(index); + case ConstPool.CONST_Double: + return "#" + index + " = int " + pool.getDoubleInfo(index); + case ConstPool.CONST_Class: + return classInfo(pool, index); + default: + throw new RuntimeException("bad LDC: " + tag); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java new file mode 100644 index 0000000..c8a9dcc --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java @@ -0,0 +1,182 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +/** + * LineNumberTable_attribute. + */ +public class LineNumberAttribute extends AttributeInfo { + /** + * The name of this attribute "LineNumberTable". + */ + public static final String tag = "LineNumberTable"; + + LineNumberAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + private LineNumberAttribute(ConstPool cp, byte[] i) { + super(cp, tag, i); + } + + /** + * Returns line_number_table_length. + * This represents the number of entries in the table. + */ + public int tableLength() { + return ByteArray.readU16bit(info, 0); + } + + /** + * Returns line_number_table[i].start_pc. + * This represents the index into the code array at which the code + * for a new line in the original source file begins. + * + * @param i the i-th entry. + */ + public int startPc(int i) { + return ByteArray.readU16bit(info, i * 4 + 2); + } + + /** + * Returns line_number_table[i].line_number. + * This represents the corresponding line number in the original + * source file. + * + * @param i the i-th entry. + */ + public int lineNumber(int i) { + return ByteArray.readU16bit(info, i * 4 + 4); + } + + /** + * Returns the line number corresponding to the specified bytecode. + * + * @param pc the index into the code array. + */ + public int toLineNumber(int pc) { + int n = tableLength(); + int i = 0; + for (; i < n; ++i) + if (pc < startPc(i)) + if (i == 0) + return lineNumber(0); + else + break; + + return lineNumber(i - 1); + } + + /** + * Returns the index into the code array at which the code for + * the specified line begins. + * + * @param line the line number. + * @return -1 if the specified line is not found. + */ + public int toStartPc(int line) { + int n = tableLength(); + for (int i = 0; i < n; ++i) + if (line == lineNumber(i)) + return startPc(i); + + return -1; + } + + /** + * Used as a return type of toNearPc(). + */ + static public class Pc { + /** + * The index into the code array. + */ + public int index; + /** + * The line number. + */ + public int line; + } + + /** + * Returns the index into the code array at which the code for + * the specified line (or the nearest line after the specified one) + * begins. + * + * @param line the line number. + * @return a pair of the index and the line number of the + * bytecode at that index. + */ + public Pc toNearPc(int line) { + int n = tableLength(); + int nearPc = 0; + int distance = 0; + if (n > 0) { + distance = lineNumber(0) - line; + nearPc = startPc(0); + } + + for (int i = 1; i < n; ++i) { + int d = lineNumber(i) - line; + if ((d < 0 && d > distance) + || (d >= 0 && (d < distance || distance < 0))) { + distance = d; + nearPc = startPc(i); + } + } + + Pc res = new Pc(); + res.index = nearPc; + res.line = line + distance; + return res; + } + + /** + * Makes a copy. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames should be null. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + byte[] src = info; + int num = src.length; + byte[] dest = new byte[num]; + for (int i = 0; i < num; ++i) + dest[i] = src[i]; + + LineNumberAttribute attr = new LineNumberAttribute(newCp, dest); + return attr; + } + + /** + * Adjusts start_pc if bytecode is inserted in a method body. + */ + void shiftPc(int where, int gapLength, boolean exclusive) { + int n = tableLength(); + for (int i = 0; i < n; ++i) { + int pos = i * 4 + 2; + int pc = ByteArray.readU16bit(info, pos); + if (pc > where || (exclusive && pc == where)) + ByteArray.write16bit(pc + gapLength, info, pos); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java new file mode 100644 index 0000000..8ce0ccd --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java @@ -0,0 +1,334 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +/** + * LocalVariableTable_attribute. + */ +public class LocalVariableAttribute extends AttributeInfo { + /** + * The name of this attribute "LocalVariableTable". + */ + public static final String tag = "LocalVariableTable"; + + /** + * The name of the attribute "LocalVariableTypeTable". + */ + public static final String typeTag = "LocalVariableTypeTable"; + + /** + * Constructs an empty LocalVariableTable. + */ + public LocalVariableAttribute(ConstPool cp) { + super(cp, tag, new byte[2]); + ByteArray.write16bit(0, info, 0); + } + + /** + * Constructs an empty LocalVariableTable. + * + * @param name the attribute name. + * LocalVariableAttribute.tag or + * LocalVariableAttribute.typeTag. + * @see #tag + * @see #typeTag + * @since 3.1 + * @deprecated + */ + public LocalVariableAttribute(ConstPool cp, String name) { + super(cp, name, new byte[2]); + ByteArray.write16bit(0, info, 0); + } + + LocalVariableAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + LocalVariableAttribute(ConstPool cp, String name, byte[] i) { + super(cp, name, i); + } + + /** + * Appends a new entry to local_variable_table. + * + * @param startPc start_pc + * @param length length + * @param nameIndex name_index + * @param descriptorIndex descriptor_index + * @param index index + */ + public void addEntry(int startPc, int length, int nameIndex, + int descriptorIndex, int index) { + int size = info.length; + byte[] newInfo = new byte[size + 10]; + ByteArray.write16bit(tableLength() + 1, newInfo, 0); + for (int i = 2; i < size; ++i) + newInfo[i] = info[i]; + + ByteArray.write16bit(startPc, newInfo, size); + ByteArray.write16bit(length, newInfo, size + 2); + ByteArray.write16bit(nameIndex, newInfo, size + 4); + ByteArray.write16bit(descriptorIndex, newInfo, size + 6); + ByteArray.write16bit(index, newInfo, size + 8); + info = newInfo; + } + + void renameClass(String oldname, String newname) { + ConstPool cp = getConstPool(); + int n = tableLength(); + for (int i = 0; i < n; ++i) { + int pos = i * 10 + 2; + int index = ByteArray.readU16bit(info, pos + 6); + if (index != 0) { + String desc = cp.getUtf8Info(index); + desc = renameEntry(desc, oldname, newname); + ByteArray.write16bit(cp.addUtf8Info(desc), info, pos + 6); + } + } + } + + String renameEntry(String desc, String oldname, String newname) { + return Descriptor.rename(desc, oldname, newname); + } + + void renameClass(Map classnames) { + ConstPool cp = getConstPool(); + int n = tableLength(); + for (int i = 0; i < n; ++i) { + int pos = i * 10 + 2; + int index = ByteArray.readU16bit(info, pos + 6); + if (index != 0) { + String desc = cp.getUtf8Info(index); + desc = renameEntry(desc, classnames); + ByteArray.write16bit(cp.addUtf8Info(desc), info, pos + 6); + } + } + } + + String renameEntry(String desc, Map classnames) { + return Descriptor.rename(desc, classnames); + } + + /** + * For each local_variable_table[i].index, + * this method increases index by delta. + * + * @param lessThan the index does not change if it + * is less than this value. + */ + public void shiftIndex(int lessThan, int delta) { + int size = info.length; + for (int i = 2; i < size; i += 10){ + int org = ByteArray.readU16bit(info, i + 8); + if (org >= lessThan) + ByteArray.write16bit(org + delta, info, i + 8); + } + } + + /** + * Returns local_variable_table_length. + * This represents the number of entries in the table. + */ + public int tableLength() { + return ByteArray.readU16bit(info, 0); + } + + /** + * Returns local_variable_table[i].start_pc. + * This represents the index into the code array from which the local + * variable is effective. + * + * @param i the i-th entry. + */ + public int startPc(int i) { + return ByteArray.readU16bit(info, i * 10 + 2); + } + + /** + * Returns local_variable_table[i].length. + * This represents the length of the code region in which the local + * variable is effective. + * + * @param i the i-th entry. + */ + public int codeLength(int i) { + return ByteArray.readU16bit(info, i * 10 + 4); + } + + /** + * Adjusts start_pc and length if bytecode is inserted in a method body. + */ + void shiftPc(int where, int gapLength, boolean exclusive) { + int n = tableLength(); + for (int i = 0; i < n; ++i) { + int pos = i * 10 + 2; + int pc = ByteArray.readU16bit(info, pos); + int len = ByteArray.readU16bit(info, pos + 2); + + /* if pc == 0, then the local variable is a method parameter. + */ + if (pc > where || (exclusive && pc == where && pc != 0)) + ByteArray.write16bit(pc + gapLength, info, pos); + else if (pc + len > where || (exclusive && pc + len == where)) + ByteArray.write16bit(len + gapLength, info, pos + 2); + } + } + + /** + * Returns the value of local_variable_table[i].name_index. + * This represents the name of the local variable. + * + * @param i the i-th entry. + */ + public int nameIndex(int i) { + return ByteArray.readU16bit(info, i * 10 + 6); + } + + /** + * Returns the name of the local variable + * specified by local_variable_table[i].name_index. + * + * @param i the i-th entry. + */ + public String variableName(int i) { + return getConstPool().getUtf8Info(nameIndex(i)); + } + + /** + * Returns the value of + * local_variable_table[i].descriptor_index. + * This represents the type descriptor of the local variable. + *

+ * If this attribute represents a LocalVariableTypeTable attribute, + * this method returns the value of + * local_variable_type_table[i].signature_index. + * It represents the type of the local variable. + * + * @param i the i-th entry. + */ + public int descriptorIndex(int i) { + return ByteArray.readU16bit(info, i * 10 + 8); + } + + /** + * This method is equivalent to descriptorIndex(). + * If this attribute represents a LocalVariableTypeTable attribute, + * this method should be used instead of descriptorIndex() + * since the method name is more appropriate. + * + * @param i the i-th entry. + * @see #descriptorIndex(int) + * @see SignatureAttribute#toFieldSignature(String) + */ + public int signatureIndex(int i) { + return descriptorIndex(i); + } + + /** + * Returns the type descriptor of the local variable + * specified by local_variable_table[i].descriptor_index. + *

+ * If this attribute represents a LocalVariableTypeTable attribute, + * this method returns the type signature of the local variable + * specified by local_variable_type_table[i].signature_index. + * + * @param i the i-th entry. + */ + public String descriptor(int i) { + return getConstPool().getUtf8Info(descriptorIndex(i)); + } + + /** + * This method is equivalent to descriptor(). + * If this attribute represents a LocalVariableTypeTable attribute, + * this method should be used instead of descriptor() + * since the method name is more appropriate. + * + *

To parse the string, call toFieldSignature(String) + * in SignatureAttribute. + * + * @param i the i-th entry. + * @see #descriptor(int) + * @see SignatureAttribute#toFieldSignature(String) + */ + public String signature(int i) { + return descriptor(i); + } + + /** + * Returns local_variable_table[i].index. + * This represents the index of the local variable. + * + * @param i the i-th entry. + */ + public int index(int i) { + return ByteArray.readU16bit(info, i * 10 + 10); + } + + /** + * Makes a copy. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames should be null. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + byte[] src = get(); + byte[] dest = new byte[src.length]; + ConstPool cp = getConstPool(); + LocalVariableAttribute attr = makeThisAttr(newCp, dest); + int n = ByteArray.readU16bit(src, 0); + ByteArray.write16bit(n, dest, 0); + int j = 2; + for (int i = 0; i < n; ++i) { + int start = ByteArray.readU16bit(src, j); + int len = ByteArray.readU16bit(src, j + 2); + int name = ByteArray.readU16bit(src, j + 4); + int type = ByteArray.readU16bit(src, j + 6); + int index = ByteArray.readU16bit(src, j + 8); + + ByteArray.write16bit(start, dest, j); + ByteArray.write16bit(len, dest, j + 2); + if (name != 0) + name = cp.copy(name, newCp, null); + + ByteArray.write16bit(name, dest, j + 4); + + if (type != 0) { + String sig = cp.getUtf8Info(type); + sig = Descriptor.rename(sig, classnames); + type = newCp.addUtf8Info(sig); + } + + ByteArray.write16bit(type, dest, j + 6); + ByteArray.write16bit(index, dest, j + 8); + j += 10; + } + + return attr; + } + + // LocalVariableTypeAttribute overrides this method. + LocalVariableAttribute makeThisAttr(ConstPool cp, byte[] dest) { + return new LocalVariableAttribute(cp, tag, dest); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java new file mode 100644 index 0000000..57bd395 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java @@ -0,0 +1,63 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +/** + * LocalVariableTypeTable_attribute. + * + * @since 3.11 + */ +public class LocalVariableTypeAttribute extends LocalVariableAttribute { + /** + * The name of the attribute "LocalVariableTypeTable". + */ + public static final String tag = LocalVariableAttribute.typeTag; + + /** + * Constructs an empty LocalVariableTypeTable. + */ + public LocalVariableTypeAttribute(ConstPool cp) { + super(cp, tag, new byte[2]); + ByteArray.write16bit(0, info, 0); + } + + LocalVariableTypeAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + private LocalVariableTypeAttribute(ConstPool cp, byte[] dest) { + super(cp, tag, dest); + } + + String renameEntry(String desc, String oldname, String newname) { + return SignatureAttribute.renameClass(desc, oldname, newname); + } + + String renameEntry(String desc, Map classnames) { + return SignatureAttribute.renameClass(desc, classnames); + } + + LocalVariableAttribute makeThisAttr(ConstPool cp, byte[] dest) { + return new LocalVariableTypeAttribute(cp, dest); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/LongVector.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/LongVector.java new file mode 100644 index 0000000..16f40e2 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/LongVector.java @@ -0,0 +1,64 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +final class LongVector { + static final int ASIZE = 128; + static final int ABITS = 7; // ASIZE = 2^ABITS + static final int VSIZE = 8; + private ConstInfo[][] objects; + private int elements; + + public LongVector() { + objects = new ConstInfo[VSIZE][]; + elements = 0; + } + + public LongVector(int initialSize) { + int vsize = ((initialSize >> ABITS) & ~(VSIZE - 1)) + VSIZE; + objects = new ConstInfo[vsize][]; + elements = 0; + } + + public int size() { return elements; } + + public int capacity() { return objects.length * ASIZE; } + + public ConstInfo elementAt(int i) { + if (i < 0 || elements <= i) + return null; + + return objects[i >> ABITS][i & (ASIZE - 1)]; + } + + public void addElement(ConstInfo value) { + int nth = elements >> ABITS; + int offset = elements & (ASIZE - 1); + int len = objects.length; + if (nth >= len) { + ConstInfo[][] newObj = new ConstInfo[len + VSIZE][]; + System.arraycopy(objects, 0, newObj, 0, len); + objects = newObj; + } + + if (objects[nth] == null) + objects[nth] = new ConstInfo[ASIZE]; + + objects[nth][offset] = value; + elements++; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodInfo.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodInfo.java new file mode 100644 index 0000000..dbe32ca --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodInfo.java @@ -0,0 +1,566 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.stackmap.MapMaker; + +/** + * method_info structure. + * + *

The bytecode sequence of the method is represented + * by a CodeAttribute object. + * + *

The following code adds the default constructor to a class: + * of int type: + *

+ * ClassFile cf = ...
+ * Bytecode code = new Bytecode(cf.getConstPool());
+ * code.addAload(0);
+ * code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V");
+ * code.addReturn(null);
+ * code.setMaxLocals(1);
+ *
+ * MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V");
+ * minfo.setCodeAttribute(code.toCodeAttribute());
+ * cf.addMethod(minfo);
+ * 
+ * + * @see #getCodeAttribute() + * @see CodeAttribute + * @see Bytecode + * @see javassist.CtMethod#getMethodInfo() + * @see javassist.CtConstructor#getMethodInfo() + */ +public class MethodInfo { + ConstPool constPool; + int accessFlags; + int name; + String cachedName; + int descriptor; + ArrayList attribute; // may be null + + /** + * If this value is true, Javassist maintains a StackMap attribute + * generated by the preverify tool of J2ME (CLDC). The initial + * value of this field is false. + */ + public static boolean doPreverify = false; + + /** + * The name of constructors: <init>. + */ + public static final String nameInit = ""; + + /** + * The name of class initializer (static initializer): + * <clinit>. + */ + public static final String nameClinit = ""; + + private MethodInfo(ConstPool cp) { + constPool = cp; + attribute = null; + } + + /** + * Constructs a method_info structure. The initial value of + * access_flags is zero. + * + * @param cp + * a constant pool table + * @param methodname + * method name + * @param desc + * method descriptor + * @see Descriptor + */ + public MethodInfo(ConstPool cp, String methodname, String desc) { + this(cp); + accessFlags = 0; + name = cp.addUtf8Info(methodname); + cachedName = methodname; + descriptor = constPool.addUtf8Info(desc); + } + + MethodInfo(ConstPool cp, DataInputStream in) throws IOException { + this(cp); + read(in); + } + + /** + * Constructs a copy of method_info structure. Class names + * appearing in the source method_info are renamed according + * to classnameMap. + * + *

+ * Note: only Code and Exceptions attributes + * are copied from the source. The other attributes are ignored. + * + * @param cp + * a constant pool table + * @param methodname + * a method name + * @param src + * a source method_info + * @param classnameMap + * specifies pairs of replaced and substituted name. + * @see Descriptor + */ + public MethodInfo(ConstPool cp, String methodname, MethodInfo src, + Map classnameMap) throws BadBytecode { + this(cp); + read(src, methodname, classnameMap); + } + + /** + * Returns a string representation of the object. + */ + public String toString() { + return getName() + " " + getDescriptor(); + } + + /** + * Copies all constant pool items to a given new constant pool + * and replaces the original items with the new ones. + * This is used for garbage collecting the items of removed fields + * and methods. + * + * @param cp the destination + */ + void compact(ConstPool cp) { + name = cp.addUtf8Info(getName()); + descriptor = cp.addUtf8Info(getDescriptor()); + attribute = AttributeInfo.copyAll(attribute, cp); + constPool = cp; + } + + void prune(ConstPool cp) { + ArrayList newAttributes = new ArrayList(); + + AttributeInfo invisibleAnnotations + = getAttribute(AnnotationsAttribute.invisibleTag); + if (invisibleAnnotations != null) { + invisibleAnnotations = invisibleAnnotations.copy(cp, null); + newAttributes.add(invisibleAnnotations); + } + + AttributeInfo visibleAnnotations + = getAttribute(AnnotationsAttribute.visibleTag); + if (visibleAnnotations != null) { + visibleAnnotations = visibleAnnotations.copy(cp, null); + newAttributes.add(visibleAnnotations); + } + + AttributeInfo parameterInvisibleAnnotations + = getAttribute(ParameterAnnotationsAttribute.invisibleTag); + if (parameterInvisibleAnnotations != null) { + parameterInvisibleAnnotations = parameterInvisibleAnnotations.copy(cp, null); + newAttributes.add(parameterInvisibleAnnotations); + } + + AttributeInfo parameterVisibleAnnotations + = getAttribute(ParameterAnnotationsAttribute.visibleTag); + if (parameterVisibleAnnotations != null) { + parameterVisibleAnnotations = parameterVisibleAnnotations.copy(cp, null); + newAttributes.add(parameterVisibleAnnotations); + } + + AnnotationDefaultAttribute defaultAttribute + = (AnnotationDefaultAttribute) getAttribute(AnnotationDefaultAttribute.tag); + if (defaultAttribute != null) + newAttributes.add(defaultAttribute); + + ExceptionsAttribute ea = getExceptionsAttribute(); + if (ea != null) + newAttributes.add(ea); + + AttributeInfo signature + = getAttribute(SignatureAttribute.tag); + if (signature != null) { + signature = signature.copy(cp, null); + newAttributes.add(signature); + } + + attribute = newAttributes; + name = cp.addUtf8Info(getName()); + descriptor = cp.addUtf8Info(getDescriptor()); + constPool = cp; + } + + /** + * Returns a method name. + */ + public String getName() { + if (cachedName == null) + cachedName = constPool.getUtf8Info(name); + + return cachedName; + } + + /** + * Sets a method name. + */ + public void setName(String newName) { + name = constPool.addUtf8Info(newName); + cachedName = newName; + } + + /** + * Returns true if this is not a constructor or a class initializer (static + * initializer). + */ + public boolean isMethod() { + String n = getName(); + return !n.equals(nameInit) && !n.equals(nameClinit); + } + + /** + * Returns a constant pool table used by this method. + */ + public ConstPool getConstPool() { + return constPool; + } + + /** + * Returns true if this is a constructor. + */ + public boolean isConstructor() { + return getName().equals(nameInit); + } + + /** + * Returns true if this is a class initializer (static initializer). + */ + public boolean isStaticInitializer() { + return getName().equals(nameClinit); + } + + /** + * Returns access flags. + * + * @see AccessFlag + */ + public int getAccessFlags() { + return accessFlags; + } + + /** + * Sets access flags. + * + * @see AccessFlag + */ + public void setAccessFlags(int acc) { + accessFlags = acc; + } + + /** + * Returns a method descriptor. + * + * @see Descriptor + */ + public String getDescriptor() { + return constPool.getUtf8Info(descriptor); + } + + /** + * Sets a method descriptor. + * + * @see Descriptor + */ + public void setDescriptor(String desc) { + if (!desc.equals(getDescriptor())) + descriptor = constPool.addUtf8Info(desc); + } + + /** + * Returns all the attributes. The returned List object + * is shared with this object. If you add a new attribute to the list, + * the attribute is also added to the method represented by this + * object. If you remove an attribute from the list, it is also removed + * from the method. + * + * @return a list of AttributeInfo objects. + * @see AttributeInfo + */ + public List getAttributes() { + if (attribute == null) + attribute = new ArrayList(); + + return attribute; + } + + /** + * Returns the attribute with the specified name. If it is not found, this + * method returns null. + * + * @param name attribute name + * @return an AttributeInfo object or null. + * @see #getAttributes() + */ + public AttributeInfo getAttribute(String name) { + return AttributeInfo.lookup(attribute, name); + } + + /** + * Appends an attribute. If there is already an attribute with the same + * name, the new one substitutes for it. + * + * @see #getAttributes() + */ + public void addAttribute(AttributeInfo info) { + if (attribute == null) + attribute = new ArrayList(); + + AttributeInfo.remove(attribute, info.getName()); + attribute.add(info); + } + + /** + * Returns an Exceptions attribute. + * + * @return an Exceptions attribute or null if it is not specified. + */ + public ExceptionsAttribute getExceptionsAttribute() { + AttributeInfo info = AttributeInfo.lookup(attribute, + ExceptionsAttribute.tag); + return (ExceptionsAttribute)info; + } + + /** + * Returns a Code attribute. + * + * @return a Code attribute or null if it is not specified. + */ + public CodeAttribute getCodeAttribute() { + AttributeInfo info = AttributeInfo.lookup(attribute, CodeAttribute.tag); + return (CodeAttribute)info; + } + + /** + * Removes an Exception attribute. + */ + public void removeExceptionsAttribute() { + AttributeInfo.remove(attribute, ExceptionsAttribute.tag); + } + + /** + * Adds an Exception attribute. + * + *

+ * The added attribute must share the same constant pool table as this + * method_info structure. + */ + public void setExceptionsAttribute(ExceptionsAttribute cattr) { + removeExceptionsAttribute(); + if (attribute == null) + attribute = new ArrayList(); + + attribute.add(cattr); + } + + /** + * Removes a Code attribute. + */ + public void removeCodeAttribute() { + AttributeInfo.remove(attribute, CodeAttribute.tag); + } + + /** + * Adds a Code attribute. + * + *

+ * The added attribute must share the same constant pool table as this + * method_info structure. + */ + public void setCodeAttribute(CodeAttribute cattr) { + removeCodeAttribute(); + if (attribute == null) + attribute = new ArrayList(); + + attribute.add(cattr); + } + + /** + * Rebuilds a stack map table if the class file is for Java 6 + * or later. Java 5 or older Java VMs do not recognize a stack + * map table. If doPreverify is true, this method + * also rebuilds a stack map for J2ME (CLDC). + * + * @param pool used for making type hierarchy. + * @param cf rebuild if this class file is for Java 6 or later. + * @see #rebuildStackMap(ClassPool) + * @see #rebuildStackMapForME(ClassPool) + * @see #doPreverify + * @since 3.6 + */ + public void rebuildStackMapIf6(ClassPool pool, ClassFile cf) + throws BadBytecode + { + if (cf.getMajorVersion() >= ClassFile.JAVA_6) + rebuildStackMap(pool); + + if (doPreverify) + rebuildStackMapForME(pool); + } + + /** + * Rebuilds a stack map table. If no stack map table is included, + * a new one is created. If this MethodInfo does not + * include a code attribute, nothing happens. + * + * @param pool used for making type hierarchy. + * @see StackMapTable + * @since 3.6 + */ + public void rebuildStackMap(ClassPool pool) throws BadBytecode { + CodeAttribute ca = getCodeAttribute(); + if (ca != null) { + StackMapTable smt = MapMaker.make(pool, this); + ca.setAttribute(smt); + } + } + + /** + * Rebuilds a stack map table for J2ME (CLDC). If no stack map table is included, + * a new one is created. If this MethodInfo does not + * include a code attribute, nothing happens. + * + * @param pool used for making type hierarchy. + * @see StackMap + * @since 3.12 + */ + public void rebuildStackMapForME(ClassPool pool) throws BadBytecode { + CodeAttribute ca = getCodeAttribute(); + if (ca != null) { + StackMap sm = MapMaker.make2(pool, this); + ca.setAttribute(sm); + } + } + + /** + * Returns the line number of the source line corresponding to the specified + * bytecode contained in this method. + * + * @param pos + * the position of the bytecode (>= 0). an index into the code + * array. + * @return -1 if this information is not available. + */ + public int getLineNumber(int pos) { + CodeAttribute ca = getCodeAttribute(); + if (ca == null) + return -1; + + LineNumberAttribute ainfo = (LineNumberAttribute)ca + .getAttribute(LineNumberAttribute.tag); + if (ainfo == null) + return -1; + + return ainfo.toLineNumber(pos); + } + + /** + * Changes a super constructor called by this constructor. + * + *

+ * This method modifies a call to super(), which should be + * at the head of a constructor body, so that a constructor in a different + * super class is called. This method does not change actual parameters. + * Hence the new super class must have a constructor with the same signature + * as the original one. + * + *

+ * This method should be called when the super class of the class declaring + * this method is changed. + * + *

+ * This method does not perform anything unless this MethodInfo + * represents a constructor. + * + * @param superclass + * the new super class + */ + public void setSuperclass(String superclass) throws BadBytecode { + if (!isConstructor()) + return; + + CodeAttribute ca = getCodeAttribute(); + byte[] code = ca.getCode(); + CodeIterator iterator = ca.iterator(); + int pos = iterator.skipSuperConstructor(); + if (pos >= 0) { // not this() + ConstPool cp = constPool; + int mref = ByteArray.readU16bit(code, pos + 1); + int nt = cp.getMethodrefNameAndType(mref); + int sc = cp.addClassInfo(superclass); + int mref2 = cp.addMethodrefInfo(sc, nt); + ByteArray.write16bit(mref2, code, pos + 1); + } + } + + private void read(MethodInfo src, String methodname, Map classnames) + throws BadBytecode { + ConstPool destCp = constPool; + accessFlags = src.accessFlags; + name = destCp.addUtf8Info(methodname); + cachedName = methodname; + ConstPool srcCp = src.constPool; + String desc = srcCp.getUtf8Info(src.descriptor); + String desc2 = Descriptor.rename(desc, classnames); + descriptor = destCp.addUtf8Info(desc2); + + attribute = new ArrayList(); + ExceptionsAttribute eattr = src.getExceptionsAttribute(); + if (eattr != null) + attribute.add(eattr.copy(destCp, classnames)); + + CodeAttribute cattr = src.getCodeAttribute(); + if (cattr != null) + attribute.add(cattr.copy(destCp, classnames)); + } + + private void read(DataInputStream in) throws IOException { + accessFlags = in.readUnsignedShort(); + name = in.readUnsignedShort(); + descriptor = in.readUnsignedShort(); + int n = in.readUnsignedShort(); + attribute = new ArrayList(); + for (int i = 0; i < n; ++i) + attribute.add(AttributeInfo.read(constPool, in)); + } + + void write(DataOutputStream out) throws IOException { + out.writeShort(accessFlags); + out.writeShort(name); + out.writeShort(descriptor); + + if (attribute == null) + out.writeShort(0); + else { + out.writeShort(attribute.size()); + AttributeInfo.writeAll(attribute, out); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java new file mode 100644 index 0000000..67ed7f6 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java @@ -0,0 +1,87 @@ +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +/** + * MethodParameters_attribute. + */ +public class MethodParametersAttribute extends AttributeInfo { + /** + * The name of this attribute "MethodParameters". + */ + public static final String tag = "MethodParameters"; + + MethodParametersAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Constructs an attribute. + * + * @param cp a constant pool table. + * @param names an array of parameter names. + * The i-th element is the name of the i-th parameter. + * @param flags an array of parameter access flags. + */ + public MethodParametersAttribute(ConstPool cp, String[] names, int[] flags) { + super(cp, tag); + byte[] data = new byte[names.length * 4 + 1]; + data[0] = (byte)names.length; + for (int i = 0; i < names.length; i++) { + ByteArray.write16bit(cp.addUtf8Info(names[i]), data, i * 4 + 1); + ByteArray.write16bit(flags[i], data, i * 4 + 3); + } + + set(data); + } + + /** + * Returns parameters_count, which is the number of + * parameters. + */ + public int size() { + return info[0] & 0xff; + } + + /** + * Returns the value of name_index of the i-th element of parameters. + * + * @param i the position of the parameter. + */ + public int name(int i) { + return ByteArray.readU16bit(info, i * 4 + 1); + } + + /** + * Returns the value of access_flags of the i-th element of parameters. + * + * @param i the position of the parameter. + * @see AccessFlag + */ + public int accessFlags(int i) { + return ByteArray.readU16bit(info, i * 4 + 3); + } + + /** + * Makes a copy. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames ignored. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + int s = size(); + ConstPool cp = getConstPool(); + String[] names = new String[s]; + int[] flags = new int[s]; + for (int i = 0; i < s; i++) { + names[i] = cp.getUtf8Info(name(i)); + flags[i] = accessFlags(i); + } + + return new MethodParametersAttribute(newCp, names, flags); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/Mnemonic.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/Mnemonic.java new file mode 100644 index 0000000..d5e465d --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/Mnemonic.java @@ -0,0 +1,239 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +/** + * JVM Instruction Names. + * + *

This interface has been separated from javassist.bytecode.Opcode + * because typical bytecode translators do not use mnemonics. If this + * interface were merged with Opcode, extra memory would be unnecessary + * consumed. + * + * @see Opcode + */ +public interface Mnemonic { + + /** + * The instruction names (mnemonics) sorted by the opcode. + * The length of this array is 202 (jsr_w=201). + */ + String[] OPCODE = { + "nop", /* 0*/ + "aconst_null", /* 1*/ + "iconst_m1", /* 2*/ + "iconst_0", /* 3*/ + "iconst_1", /* 4*/ + "iconst_2", /* 5*/ + "iconst_3", /* 6*/ + "iconst_4", /* 7*/ + "iconst_5", /* 8*/ + "lconst_0", /* 9*/ + "lconst_1", /* 10*/ + "fconst_0", /* 11*/ + "fconst_1", /* 12*/ + "fconst_2", /* 13*/ + "dconst_0", /* 14*/ + "dconst_1", /* 15*/ + "bipush", /* 16*/ + "sipush", /* 17*/ + "ldc", /* 18*/ + "ldc_w", /* 19*/ + "ldc2_w", /* 20*/ + "iload", /* 21*/ + "lload", /* 22*/ + "fload", /* 23*/ + "dload", /* 24*/ + "aload", /* 25*/ + "iload_0", /* 26*/ + "iload_1", /* 27*/ + "iload_2", /* 28*/ + "iload_3", /* 29*/ + "lload_0", /* 30*/ + "lload_1", /* 31*/ + "lload_2", /* 32*/ + "lload_3", /* 33*/ + "fload_0", /* 34*/ + "fload_1", /* 35*/ + "fload_2", /* 36*/ + "fload_3", /* 37*/ + "dload_0", /* 38*/ + "dload_1", /* 39*/ + "dload_2", /* 40*/ + "dload_3", /* 41*/ + "aload_0", /* 42*/ + "aload_1", /* 43*/ + "aload_2", /* 44*/ + "aload_3", /* 45*/ + "iaload", /* 46*/ + "laload", /* 47*/ + "faload", /* 48*/ + "daload", /* 49*/ + "aaload", /* 50*/ + "baload", /* 51*/ + "caload", /* 52*/ + "saload", /* 53*/ + "istore", /* 54*/ + "lstore", /* 55*/ + "fstore", /* 56*/ + "dstore", /* 57*/ + "astore", /* 58*/ + "istore_0", /* 59*/ + "istore_1", /* 60*/ + "istore_2", /* 61*/ + "istore_3", /* 62*/ + "lstore_0", /* 63*/ + "lstore_1", /* 64*/ + "lstore_2", /* 65*/ + "lstore_3", /* 66*/ + "fstore_0", /* 67*/ + "fstore_1", /* 68*/ + "fstore_2", /* 69*/ + "fstore_3", /* 70*/ + "dstore_0", /* 71*/ + "dstore_1", /* 72*/ + "dstore_2", /* 73*/ + "dstore_3", /* 74*/ + "astore_0", /* 75*/ + "astore_1", /* 76*/ + "astore_2", /* 77*/ + "astore_3", /* 78*/ + "iastore", /* 79*/ + "lastore", /* 80*/ + "fastore", /* 81*/ + "dastore", /* 82*/ + "aastore", /* 83*/ + "bastore", /* 84*/ + "castore", /* 85*/ + "sastore", /* 86*/ + "pop", /* 87*/ + "pop2", /* 88*/ + "dup", /* 89*/ + "dup_x1", /* 90*/ + "dup_x2", /* 91*/ + "dup2", /* 92*/ + "dup2_x1", /* 93*/ + "dup2_x2", /* 94*/ + "swap", /* 95*/ + "iadd", /* 96*/ + "ladd", /* 97*/ + "fadd", /* 98*/ + "dadd", /* 99*/ + "isub", /* 100*/ + "lsub", /* 101*/ + "fsub", /* 102*/ + "dsub", /* 103*/ + "imul", /* 104*/ + "lmul", /* 105*/ + "fmul", /* 106*/ + "dmul", /* 107*/ + "idiv", /* 108*/ + "ldiv", /* 109*/ + "fdiv", /* 110*/ + "ddiv", /* 111*/ + "irem", /* 112*/ + "lrem", /* 113*/ + "frem", /* 114*/ + "drem", /* 115*/ + "ineg", /* 116*/ + "lneg", /* 117*/ + "fneg", /* 118*/ + "dneg", /* 119*/ + "ishl", /* 120*/ + "lshl", /* 121*/ + "ishr", /* 122*/ + "lshr", /* 123*/ + "iushr", /* 124*/ + "lushr", /* 125*/ + "iand", /* 126*/ + "land", /* 127*/ + "ior", /* 128*/ + "lor", /* 129*/ + "ixor", /* 130*/ + "lxor", /* 131*/ + "iinc", /* 132*/ + "i2l", /* 133*/ + "i2f", /* 134*/ + "i2d", /* 135*/ + "l2i", /* 136*/ + "l2f", /* 137*/ + "l2d", /* 138*/ + "f2i", /* 139*/ + "f2l", /* 140*/ + "f2d", /* 141*/ + "d2i", /* 142*/ + "d2l", /* 143*/ + "d2f", /* 144*/ + "i2b", /* 145*/ + "i2c", /* 146*/ + "i2s", /* 147*/ + "lcmp", /* 148*/ + "fcmpl", /* 149*/ + "fcmpg", /* 150*/ + "dcmpl", /* 151*/ + "dcmpg", /* 152*/ + "ifeq", /* 153*/ + "ifne", /* 154*/ + "iflt", /* 155*/ + "ifge", /* 156*/ + "ifgt", /* 157*/ + "ifle", /* 158*/ + "if_icmpeq", /* 159*/ + "if_icmpne", /* 160*/ + "if_icmplt", /* 161*/ + "if_icmpge", /* 162*/ + "if_icmpgt", /* 163*/ + "if_icmple", /* 164*/ + "if_acmpeq", /* 165*/ + "if_acmpne", /* 166*/ + "goto", /* 167*/ + "jsr", /* 168*/ + "ret", /* 169*/ + "tableswitch", /* 170*/ + "lookupswitch", /* 171*/ + "ireturn", /* 172*/ + "lreturn", /* 173*/ + "freturn", /* 174*/ + "dreturn", /* 175*/ + "areturn", /* 176*/ + "return", /* 177*/ + "getstatic", /* 178*/ + "putstatic", /* 179*/ + "getfield", /* 180*/ + "putfield", /* 181*/ + "invokevirtual", /* 182*/ + "invokespecial", /* 183*/ + "invokestatic", /* 184*/ + "invokeinterface", /* 185*/ + "invokedynamic", /* 186 */ + "new", /* 187*/ + "newarray", /* 188*/ + "anewarray", /* 189*/ + "arraylength", /* 190*/ + "athrow", /* 191*/ + "checkcast", /* 192*/ + "instanceof", /* 193*/ + "monitorenter", /* 194*/ + "monitorexit", /* 195*/ + "wide", /* 196*/ + "multianewarray", /* 197*/ + "ifnull", /* 198*/ + "ifnonnull", /* 199*/ + "goto_w", /* 200*/ + "jsr_w" /* 201*/ + }; +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/Opcode.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/Opcode.java new file mode 100644 index 0000000..0a8ea3a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/Opcode.java @@ -0,0 +1,449 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +/** + * JVM Instruction Set. + * + *

This interface defines opcodes and + * array types for the NEWARRAY instruction. + * + * @see Mnemonic + */ +public interface Opcode { + /* Opcodes */ + + int AALOAD = 50; + int AASTORE = 83; + int ACONST_NULL = 1; + int ALOAD = 25; + int ALOAD_0 = 42; + int ALOAD_1 = 43; + int ALOAD_2 = 44; + int ALOAD_3 = 45; + int ANEWARRAY = 189; + int ARETURN = 176; + int ARRAYLENGTH = 190; + int ASTORE = 58; + int ASTORE_0 = 75; + int ASTORE_1 = 76; + int ASTORE_2 = 77; + int ASTORE_3 = 78; + int ATHROW = 191; + int BALOAD = 51; + int BASTORE = 84; + int BIPUSH = 16; + int CALOAD = 52; + int CASTORE = 85; + int CHECKCAST = 192; + int D2F = 144; + int D2I = 142; + int D2L = 143; + int DADD = 99; + int DALOAD = 49; + int DASTORE = 82; + int DCMPG = 152; + int DCMPL = 151; + int DCONST_0 = 14; + int DCONST_1 = 15; + int DDIV = 111; + int DLOAD = 24; + int DLOAD_0 = 38; + int DLOAD_1 = 39; + int DLOAD_2 = 40; + int DLOAD_3 = 41; + int DMUL = 107; + int DNEG = 119; + int DREM = 115; + int DRETURN = 175; + int DSTORE = 57; + int DSTORE_0 = 71; + int DSTORE_1 = 72; + int DSTORE_2 = 73; + int DSTORE_3 = 74; + int DSUB = 103; + int DUP = 89; + int DUP2 = 92; + int DUP2_X1 = 93; + int DUP2_X2 = 94; + int DUP_X1 = 90; + int DUP_X2 = 91; + int F2D = 141; + int F2I = 139; + int F2L = 140; + int FADD = 98; + int FALOAD = 48; + int FASTORE = 81; + int FCMPG = 150; + int FCMPL = 149; + int FCONST_0 = 11; + int FCONST_1 = 12; + int FCONST_2 = 13; + int FDIV = 110; + int FLOAD = 23; + int FLOAD_0 = 34; + int FLOAD_1 = 35; + int FLOAD_2 = 36; + int FLOAD_3 = 37; + int FMUL = 106; + int FNEG = 118; + int FREM = 114; + int FRETURN = 174; + int FSTORE = 56; + int FSTORE_0 = 67; + int FSTORE_1 = 68; + int FSTORE_2 = 69; + int FSTORE_3 = 70; + int FSUB = 102; + int GETFIELD = 180; + int GETSTATIC = 178; + int GOTO = 167; + int GOTO_W = 200; + int I2B = 145; + int I2C = 146; + int I2D = 135; + int I2F = 134; + int I2L = 133; + int I2S = 147; + int IADD = 96; + int IALOAD = 46; + int IAND = 126; + int IASTORE = 79; + 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 ICONST_M1 = 2; + int IDIV = 108; + int IFEQ = 153; + int IFGE = 156; + int IFGT = 157; + int IFLE = 158; + int IFLT = 155; + int IFNE = 154; + int IFNONNULL = 199; + int IFNULL = 198; + int IF_ACMPEQ = 165; + int IF_ACMPNE = 166; + int IF_ICMPEQ = 159; + int IF_ICMPGE = 162; + int IF_ICMPGT = 163; + int IF_ICMPLE = 164; + int IF_ICMPLT = 161; + int IF_ICMPNE = 160; + int IINC = 132; + int ILOAD = 21; + int ILOAD_0 = 26; + int ILOAD_1 = 27; + int ILOAD_2 = 28; + int ILOAD_3 = 29; + int IMUL = 104; + int INEG = 116; + int INSTANCEOF = 193; + int INVOKEDYNAMIC = 186; + int INVOKEINTERFACE = 185; + int INVOKESPECIAL = 183; + int INVOKESTATIC = 184; + int INVOKEVIRTUAL = 182; + int IOR = 128; + int IREM = 112; + int IRETURN = 172; + int ISHL = 120; + int ISHR = 122; + int ISTORE = 54; + int ISTORE_0 = 59; + int ISTORE_1 = 60; + int ISTORE_2 = 61; + int ISTORE_3 = 62; + int ISUB = 100; + int IUSHR = 124; + int IXOR = 130; + int JSR = 168; + int JSR_W = 201; + int L2D = 138; + int L2F = 137; + int L2I = 136; + int LADD = 97; + int LALOAD = 47; + int LAND = 127; + int LASTORE = 80; + int LCMP = 148; + int LCONST_0 = 9; + int LCONST_1 = 10; + int LDC = 18; + int LDC2_W = 20; + int LDC_W = 19; + int LDIV = 109; + int LLOAD = 22; + int LLOAD_0 = 30; + int LLOAD_1 = 31; + int LLOAD_2 = 32; + int LLOAD_3 = 33; + int LMUL = 105; + int LNEG = 117; + int LOOKUPSWITCH = 171; + int LOR = 129; + int LREM = 113; + int LRETURN = 173; + int LSHL = 121; + int LSHR = 123; + int LSTORE = 55; + int LSTORE_0 = 63; + int LSTORE_1 = 64; + int LSTORE_2 = 65; + int LSTORE_3 = 66; + int LSUB = 101; + int LUSHR = 125; + int LXOR = 131; + int MONITORENTER = 194; + int MONITOREXIT = 195; + int MULTIANEWARRAY = 197; + int NEW = 187; + int NEWARRAY = 188; + int NOP = 0; + int POP = 87; + int POP2 = 88; + int PUTFIELD = 181; + int PUTSTATIC = 179; + int RET = 169; + int RETURN = 177; + int SALOAD = 53; + int SASTORE = 86; + int SIPUSH = 17; + int SWAP = 95; + int TABLESWITCH = 170; + int WIDE = 196; + + /* array-type code for the newarray instruction */ + + 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; + + /* how many values are pushed on the operand stack. */ + int[] STACK_GROW = { + 0, // nop, 0 + 1, // aconst_null, 1 + 1, // iconst_m1, 2 + 1, // iconst_0, 3 + 1, // iconst_1, 4 + 1, // iconst_2, 5 + 1, // iconst_3, 6 + 1, // iconst_4, 7 + 1, // iconst_5, 8 + 2, // lconst_0, 9 + 2, // lconst_1, 10 + 1, // fconst_0, 11 + 1, // fconst_1, 12 + 1, // fconst_2, 13 + 2, // dconst_0, 14 + 2, // dconst_1, 15 + 1, // bipush, 16 + 1, // sipush, 17 + 1, // ldc, 18 + 1, // ldc_w, 19 + 2, // ldc2_w, 20 + 1, // iload, 21 + 2, // lload, 22 + 1, // fload, 23 + 2, // dload, 24 + 1, // aload, 25 + 1, // iload_0, 26 + 1, // iload_1, 27 + 1, // iload_2, 28 + 1, // iload_3, 29 + 2, // lload_0, 30 + 2, // lload_1, 31 + 2, // lload_2, 32 + 2, // lload_3, 33 + 1, // fload_0, 34 + 1, // fload_1, 35 + 1, // fload_2, 36 + 1, // fload_3, 37 + 2, // dload_0, 38 + 2, // dload_1, 39 + 2, // dload_2, 40 + 2, // dload_3, 41 + 1, // aload_0, 42 + 1, // aload_1, 43 + 1, // aload_2, 44 + 1, // aload_3, 45 + -1, // iaload, 46 + 0, // laload, 47 + -1, // faload, 48 + 0, // daload, 49 + -1, // aaload, 50 + -1, // baload, 51 + -1, // caload, 52 + -1, // saload, 53 + -1, // istore, 54 + -2, // lstore, 55 + -1, // fstore, 56 + -2, // dstore, 57 + -1, // astore, 58 + -1, // istore_0, 59 + -1, // istore_1, 60 + -1, // istore_2, 61 + -1, // istore_3, 62 + -2, // lstore_0, 63 + -2, // lstore_1, 64 + -2, // lstore_2, 65 + -2, // lstore_3, 66 + -1, // fstore_0, 67 + -1, // fstore_1, 68 + -1, // fstore_2, 69 + -1, // fstore_3, 70 + -2, // dstore_0, 71 + -2, // dstore_1, 72 + -2, // dstore_2, 73 + -2, // dstore_3, 74 + -1, // astore_0, 75 + -1, // astore_1, 76 + -1, // astore_2, 77 + -1, // astore_3, 78 + -3, // iastore, 79 + -4, // lastore, 80 + -3, // fastore, 81 + -4, // dastore, 82 + -3, // aastore, 83 + -3, // bastore, 84 + -3, // castore, 85 + -3, // sastore, 86 + -1, // pop, 87 + -2, // pop2, 88 + 1, // dup, 89 + 1, // dup_x1, 90 + 1, // dup_x2, 91 + 2, // dup2, 92 + 2, // dup2_x1, 93 + 2, // dup2_x2, 94 + 0, // swap, 95 + -1, // iadd, 96 + -2, // ladd, 97 + -1, // fadd, 98 + -2, // dadd, 99 + -1, // isub, 100 + -2, // lsub, 101 + -1, // fsub, 102 + -2, // dsub, 103 + -1, // imul, 104 + -2, // lmul, 105 + -1, // fmul, 106 + -2, // dmul, 107 + -1, // idiv, 108 + -2, // ldiv, 109 + -1, // fdiv, 110 + -2, // ddiv, 111 + -1, // irem, 112 + -2, // lrem, 113 + -1, // frem, 114 + -2, // drem, 115 + 0, // ineg, 116 + 0, // lneg, 117 + 0, // fneg, 118 + 0, // dneg, 119 + -1, // ishl, 120 + -1, // lshl, 121 + -1, // ishr, 122 + -1, // lshr, 123 + -1, // iushr, 124 + -1, // lushr, 125 + -1, // iand, 126 + -2, // land, 127 + -1, // ior, 128 + -2, // lor, 129 + -1, // ixor, 130 + -2, // lxor, 131 + 0, // iinc, 132 + 1, // i2l, 133 + 0, // i2f, 134 + 1, // i2d, 135 + -1, // l2i, 136 + -1, // l2f, 137 + 0, // l2d, 138 + 0, // f2i, 139 + 1, // f2l, 140 + 1, // f2d, 141 + -1, // d2i, 142 + 0, // d2l, 143 + -1, // d2f, 144 + 0, // i2b, 145 + 0, // i2c, 146 + 0, // i2s, 147 + -3, // lcmp, 148 + -1, // fcmpl, 149 + -1, // fcmpg, 150 + -3, // dcmpl, 151 + -3, // dcmpg, 152 + -1, // ifeq, 153 + -1, // ifne, 154 + -1, // iflt, 155 + -1, // ifge, 156 + -1, // ifgt, 157 + -1, // ifle, 158 + -2, // if_icmpeq, 159 + -2, // if_icmpne, 160 + -2, // if_icmplt, 161 + -2, // if_icmpge, 162 + -2, // if_icmpgt, 163 + -2, // if_icmple, 164 + -2, // if_acmpeq, 165 + -2, // if_acmpne, 166 + 0, // goto, 167 + 1, // jsr, 168 + 0, // ret, 169 + -1, // tableswitch, 170 + -1, // lookupswitch, 171 + -1, // ireturn, 172 + -2, // lreturn, 173 + -1, // freturn, 174 + -2, // dreturn, 175 + -1, // areturn, 176 + 0, // return, 177 + 0, // getstatic, 178 depends on the type + 0, // putstatic, 179 depends on the type + 0, // getfield, 180 depends on the type + 0, // putfield, 181 depends on the type + 0, // invokevirtual, 182 depends on the type + 0, // invokespecial, 183 depends on the type + 0, // invokestatic, 184 depends on the type + 0, // invokeinterface, 185 depends on the type + 0, // invokedynaimc, 186 depends on the type + 1, // new, 187 + 0, // newarray, 188 + 0, // anewarray, 189 + 0, // arraylength, 190 + -1, // athrow, 191 stack is cleared + 0, // checkcast, 192 + 0, // instanceof, 193 + -1, // monitorenter, 194 + -1, // monitorexit, 195 + 0, // wide, 196 depends on the following opcode + 0, // multianewarray, 197 depends on the dimensions + -1, // ifnull, 198 + -1, // ifnonnull, 199 + 0, // goto_w, 200 + 1 // jsr_w, 201 + }; +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java new file mode 100644 index 0000000..cdefada --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java @@ -0,0 +1,215 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.util.HashMap; +import java.util.Map; +import java.io.IOException; +import java.io.DataInputStream; +import java.io.ByteArrayOutputStream; + +import com.wenshuo.agent.javassist.bytecode.AnnotationsAttribute.Copier; +import com.wenshuo.agent.javassist.bytecode.AnnotationsAttribute.Parser; +import com.wenshuo.agent.javassist.bytecode.AnnotationsAttribute.Renamer; +import com.wenshuo.agent.javassist.bytecode.annotation.*; + +/** + * A class representing RuntimeVisibleAnnotations_attribute and + * RuntimeInvisibleAnnotations_attribute. + * + *

To obtain an ParameterAnnotationAttribute object, invoke + * getAttribute(ParameterAnnotationsAttribute.invisibleTag) + * in MethodInfo. + * The obtained attribute is a + * runtime invisible annotations attribute. + * If the parameter is + * ParameterAnnotationAttribute.visibleTag, then the obtained + * attribute is a runtime visible one. + */ +public class ParameterAnnotationsAttribute extends AttributeInfo { + /** + * The name of the RuntimeVisibleParameterAnnotations + * attribute. + */ + public static final String visibleTag + = "RuntimeVisibleParameterAnnotations"; + + /** + * The name of the RuntimeInvisibleParameterAnnotations + * attribute. + */ + public static final String invisibleTag + = "RuntimeInvisibleParameterAnnotations"; + /** + * Constructs + * a Runtime(In)VisibleParameterAnnotations_attribute. + * + * @param cp constant pool + * @param attrname attribute name (visibleTag or + * invisibleTag). + * @param info the contents of this attribute. It does not + * include attribute_name_index or + * attribute_length. + */ + public ParameterAnnotationsAttribute(ConstPool cp, String attrname, + byte[] info) { + super(cp, attrname, info); + } + + /** + * Constructs an empty + * Runtime(In)VisibleParameterAnnotations_attribute. + * A new annotation can be later added to the created attribute + * by setAnnotations(). + * + * @param cp constant pool + * @param attrname attribute name (visibleTag or + * invisibleTag). + * @see #setAnnotations(Annotation[][]) + */ + public ParameterAnnotationsAttribute(ConstPool cp, String attrname) { + this(cp, attrname, new byte[] { 0 }); + } + + /** + * @param n the attribute name. + */ + ParameterAnnotationsAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Returns num_parameters. + */ + public int numParameters() { + return info[0] & 0xff; + } + + /** + * Copies this attribute and returns a new copy. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + Copier copier = new Copier(info, constPool, newCp, classnames); + try { + copier.parameters(); + return new ParameterAnnotationsAttribute(newCp, getName(), + copier.close()); + } + catch (Exception e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Parses the annotations and returns a data structure representing + * that parsed annotations. Note that changes of the node values of the + * returned tree are not reflected on the annotations represented by + * this object unless the tree is copied back to this object by + * setAnnotations(). + * + * @return Each element of the returned array represents an array of + * annotations that are associated with each method parameter. + * + * @see #setAnnotations(Annotation[][]) + */ + public Annotation[][] getAnnotations() { + try { + return new Parser(info, constPool).parseParameters(); + } + catch (Exception e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Changes the annotations represented by this object according to + * the given array of Annotation objects. + * + * @param params the data structure representing the + * new annotations. Every element of this array + * is an array of Annotation and + * it represens annotations of each method parameter. + */ + public void setAnnotations(Annotation[][] params) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + AnnotationsWriter writer = new AnnotationsWriter(output, constPool); + try { + int n = params.length; + writer.numParameters(n); + for (int i = 0; i < n; ++i) { + Annotation[] anno = params[i]; + writer.numAnnotations(anno.length); + for (int j = 0; j < anno.length; ++j) + anno[j].write(writer); + } + + writer.close(); + } + catch (IOException e) { + throw new RuntimeException(e); // should never reach here. + } + + set(output.toByteArray()); + } + + /** + * @param oldname a JVM class name. + * @param newname a JVM class name. + */ + void renameClass(String oldname, String newname) { + HashMap map = new HashMap(); + map.put(oldname, newname); + renameClass(map); + } + + void renameClass(Map classnames) { + Renamer renamer = new Renamer(info, getConstPool(), classnames); + try { + renamer.parameters(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + void getRefClasses(Map classnames) { renameClass(classnames); } + + /** + * Returns a string representation of this object. + */ + public String toString() { + Annotation[][] aa = getAnnotations(); + StringBuilder sbuf = new StringBuilder(); + int k = 0; + while (k < aa.length) { + Annotation[] a = aa[k++]; + int i = 0; + while (i < a.length) { + sbuf.append(a[i++].toString()); + if (i != a.length) + sbuf.append(" "); + } + + if (k != aa.length) + sbuf.append(", "); + } + + return sbuf.toString(); + + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java new file mode 100644 index 0000000..f560505 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java @@ -0,0 +1,1160 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; +import java.util.ArrayList; +import com.wenshuo.agent.javassist.CtClass; + +/** + * Signature_attribute. + */ +public class SignatureAttribute extends AttributeInfo { + /** + * The name of this attribute "Signature". + */ + public static final String tag = "Signature"; + + SignatureAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Constructs a Signature attribute. + * + * @param cp a constant pool table. + * @param signature the signature represented by this attribute. + */ + public SignatureAttribute(ConstPool cp, String signature) { + super(cp, tag); + int index = cp.addUtf8Info(signature); + byte[] bvalue = new byte[2]; + bvalue[0] = (byte)(index >>> 8); + bvalue[1] = (byte)index; + set(bvalue); + } + + /** + * Returns the generic signature indicated by signature_index. + * + * @see #toClassSignature(String) + * @see #toMethodSignature(String) + * @see #toFieldSignature(String) + */ + public String getSignature() { + return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0)); + } + + /** + * Sets signature_index to the index of the given generic signature, + * which is added to a constant pool. + * + * @param sig new signature. + * @since 3.11 + */ + public void setSignature(String sig) { + int index = getConstPool().addUtf8Info(sig); + ByteArray.write16bit(index, info, 0); + } + + /** + * Makes a copy. Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + return new SignatureAttribute(newCp, getSignature()); + } + + void renameClass(String oldname, String newname) { + String sig = renameClass(getSignature(), oldname, newname); + setSignature(sig); + } + + void renameClass(Map classnames) { + String sig = renameClass(getSignature(), classnames); + setSignature(sig); + } + + static String renameClass(String desc, String oldname, String newname) { + Map map = new java.util.HashMap(); + map.put(oldname, newname); + return renameClass(desc, map); + } + + static String renameClass(String desc, Map map) { + if (map == null) + return desc; + + StringBuilder newdesc = new StringBuilder(); + int head = 0; + int i = 0; + for (;;) { + int j = desc.indexOf('L', i); + if (j < 0) + break; + + StringBuilder nameBuf = new StringBuilder(); + int k = j; + char c; + try { + while ((c = desc.charAt(++k)) != ';') { + nameBuf.append(c); + if (c == '<') { + while ((c = desc.charAt(++k)) != '>') + nameBuf.append(c); + + nameBuf.append(c); + } + } + } + catch (IndexOutOfBoundsException e) { break; } + i = k + 1; + String name = nameBuf.toString(); + String name2 = (String)map.get(name); + if (name2 != null) { + newdesc.append(desc.substring(head, j)); + newdesc.append('L'); + newdesc.append(name2); + newdesc.append(c); + head = i; + } + } + + if (head == 0) + return desc; + else { + int len = desc.length(); + if (head < len) + newdesc.append(desc.substring(head, len)); + + return newdesc.toString(); + } + } + + private static boolean isNamePart(int c) { + return c != ';' && c != '<'; + } + + static private class Cursor { + int position = 0; + + int indexOf(String s, int ch) throws BadBytecode { + int i = s.indexOf(ch, position); + if (i < 0) + throw error(s); + else { + position = i + 1; + return i; + } + } + } + + /** + * Class signature. + */ + public static class ClassSignature { + TypeParameter[] params; + ClassType superClass; + ClassType[] interfaces; + + /** + * Constructs a class signature. + * + * @param params type parameters. + * @param superClass the super class. + * @param interfaces the interface types. + */ + public ClassSignature(TypeParameter[] params, ClassType superClass, ClassType[] interfaces) { + this.params = params == null ? new TypeParameter[0] : params; + this.superClass = superClass == null ? ClassType.OBJECT : superClass; + this.interfaces = interfaces == null ? new ClassType[0] : interfaces; + } + + /** + * Constructs a class signature. + * + * @param p type parameters. + */ + public ClassSignature(TypeParameter[] p) { + this(p, null, null); + } + + /** + * Returns the type parameters. + * + * @return a zero-length array if the type parameters are not specified. + */ + public TypeParameter[] getParameters() { + return params; + } + + /** + * Returns the super class. + */ + public ClassType getSuperClass() { return superClass; } + + /** + * Returns the super interfaces. + * + * @return a zero-length array if the super interfaces are not specified. + */ + public ClassType[] getInterfaces() { return interfaces; } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(); + + TypeParameter.toString(sbuf, params); + sbuf.append(" extends ").append(superClass); + if (interfaces.length > 0) { + sbuf.append(" implements "); + Type.toString(sbuf, interfaces); + } + + return sbuf.toString(); + } + + /** + * Returns the encoded string representing the method type signature. + */ + public String encode() { + StringBuffer sbuf = new StringBuffer(); + if (params.length > 0) { + sbuf.append('<'); + for (int i = 0; i < params.length; i++) + params[i].encode(sbuf); + + sbuf.append('>'); + } + + superClass.encode(sbuf); + for (int i = 0; i < interfaces.length; i++) + interfaces[i].encode(sbuf); + + return sbuf.toString(); + } + } + + /** + * Method type signature. + */ + public static class MethodSignature { + TypeParameter[] typeParams; + Type[] params; + Type retType; + ObjectType[] exceptions; + + /** + * Constructs a method type signature. Any parameter can be null + * to represent void or nothing. + * + * @param tp type parameters. + * @param params parameter types. + * @param ret a return type, or null if the return type is void. + * @param ex exception types. + */ + public MethodSignature(TypeParameter[] tp, Type[] params, Type ret, ObjectType[] ex) { + typeParams = tp == null ? new TypeParameter[0] : tp; + this.params = params == null ? new Type[0] : params; + retType = ret == null ? new BaseType("void") : ret; + exceptions = ex == null ? new ObjectType[0] : ex; + } + + /** + * Returns the formal type parameters. + * + * @return a zero-length array if the type parameters are not specified. + */ + public TypeParameter[] getTypeParameters() { return typeParams; } + + /** + * Returns the types of the formal parameters. + * + * @return a zero-length array if no formal parameter is taken. + */ + public Type[] getParameterTypes() { return params; } + + /** + * Returns the type of the returned value. + */ + public Type getReturnType() { return retType; } + + /** + * Returns the types of the exceptions that may be thrown. + * + * @return a zero-length array if exceptions are never thrown or + * the exception types are not parameterized types or type variables. + */ + public ObjectType[] getExceptionTypes() { return exceptions; } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(); + + TypeParameter.toString(sbuf, typeParams); + sbuf.append(" ("); + Type.toString(sbuf, params); + sbuf.append(") "); + sbuf.append(retType); + if (exceptions.length > 0) { + sbuf.append(" throws "); + Type.toString(sbuf, exceptions); + } + + return sbuf.toString(); + } + + /** + * Returns the encoded string representing the method type signature. + */ + public String encode() { + StringBuffer sbuf = new StringBuffer(); + if (typeParams.length > 0) { + sbuf.append('<'); + for (int i = 0; i < typeParams.length; i++) + typeParams[i].encode(sbuf); + + sbuf.append('>'); + } + + sbuf.append('('); + for (int i = 0; i < params.length; i++) + params[i].encode(sbuf); + + sbuf.append(')'); + retType.encode(sbuf); + if (exceptions.length > 0) + for (int i = 0; i < exceptions.length; i++) { + sbuf.append('^'); + exceptions[i].encode(sbuf); + } + + return sbuf.toString(); + } + } + + /** + * Formal type parameters. + * + * @see TypeArgument + */ + public static class TypeParameter { + String name; + ObjectType superClass; + ObjectType[] superInterfaces; + + TypeParameter(String sig, int nb, int ne, ObjectType sc, ObjectType[] si) { + name = sig.substring(nb, ne); + superClass = sc; + superInterfaces = si; + } + + /** + * Constructs a TypeParameter representing a type parametre + * like <T extends ... >. + * + * @param name parameter name. + * @param superClass an upper bound class-type (or null). + * @param superInterfaces an upper bound interface-type (or null). + */ + public TypeParameter(String name, ObjectType superClass, ObjectType[] superInterfaces) { + this.name = name; + this.superClass = superClass; + if (superInterfaces == null) + this.superInterfaces = new ObjectType[0]; + else + this.superInterfaces = superInterfaces; + } + + /** + * Constructs a TypeParameter representing a type parameter + * like <T>. + * + * @param name parameter name. + */ + public TypeParameter(String name) { + this(name, null, null); + } + + /** + * Returns the name of the type parameter. + */ + public String getName() { + return name; + } + + /** + * Returns the class bound of this parameter. + */ + public ObjectType getClassBound() { return superClass; } + + /** + * Returns the interface bound of this parameter. + * + * @return a zero-length array if the interface bound is not specified. + */ + public ObjectType[] getInterfaceBound() { return superInterfaces; } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(getName()); + if (superClass != null) + sbuf.append(" extends ").append(superClass.toString()); + + int len = superInterfaces.length; + if (len > 0) { + for (int i = 0; i < len; i++) { + if (i > 0 || superClass != null) + sbuf.append(" & "); + else + sbuf.append(" extends "); + + sbuf.append(superInterfaces[i].toString()); + } + } + + return sbuf.toString(); + } + + static void toString(StringBuffer sbuf, TypeParameter[] tp) { + sbuf.append('<'); + for (int i = 0; i < tp.length; i++) { + if (i > 0) + sbuf.append(", "); + + sbuf.append(tp[i]); + } + + sbuf.append('>'); + } + + void encode(StringBuffer sb) { + sb.append(name); + if (superClass == null) + sb.append(":Ljava/lang/Object;"); + else { + sb.append(':'); + superClass.encode(sb); + } + + for (int i = 0; i < superInterfaces.length; i++) { + sb.append(':'); + superInterfaces[i].encode(sb); + } + } + } + + /** + * Type argument. + * + * @see TypeParameter + */ + public static class TypeArgument { + ObjectType arg; + char wildcard; + + TypeArgument(ObjectType a, char w) { + arg = a; + wildcard = w; + } + + /** + * Constructs a TypeArgument. + * A type argument is <String>, <int[]>, + * or a type variable <T>, etc. + * + * @param t a class type, an array type, or a type variable. + */ + public TypeArgument(ObjectType t) { + this(t, ' '); + } + + /** + * Constructs a TypeArgument representing <?>. + */ + public TypeArgument() { + this(null, '*'); + } + + /** + * A factory method constructing a TypeArgument with an upper bound. + * It represents <? extends ... > + * + * @param t an upper bound type. + */ + public static TypeArgument subclassOf(ObjectType t) { + return new TypeArgument(t, '+'); + } + + /** + * A factory method constructing a TypeArgument with an lower bound. + * It represents <? super ... > + * + * @param t an lower bbound type. + */ + public static TypeArgument superOf(ObjectType t) { + return new TypeArgument(t, '-'); + } + + /** + * Returns the kind of this type argument. + * + * @return ' ' (not-wildcard), '*' (wildcard), '+' (wildcard with + * upper bound), or '-' (wildcard with lower bound). + */ + public char getKind() { return wildcard; } + + /** + * Returns true if this type argument is a wildcard type + * such as ?, ? extends String, or ? super Integer. + */ + public boolean isWildcard() { return wildcard != ' '; } + + /** + * Returns the type represented by this argument + * if the argument is not a wildcard type. Otherwise, this method + * returns the upper bound (if the kind is '+'), + * the lower bound (if the kind is '-'), or null (if the upper or lower + * bound is not specified). + */ + public ObjectType getType() { return arg; } + + /** + * Returns the string representation. + */ + public String toString() { + if (wildcard == '*') + return "?"; + + String type = arg.toString(); + if (wildcard == ' ') + return type; + else if (wildcard == '+') + return "? extends " + type; + else + return "? super " + type; + } + + static void encode(StringBuffer sb, TypeArgument[] args) { + sb.append('<'); + for (int i = 0; i < args.length; i++) { + TypeArgument ta = args[i]; + if (ta.isWildcard()) + sb.append(ta.wildcard); + + if (ta.getType() != null) + ta.getType().encode(sb); + } + + sb.append('>'); + } + } + + /** + * Primitive types and object types. + */ + public static abstract class Type { + abstract void encode(StringBuffer sb); + static void toString(StringBuffer sbuf, Type[] ts) { + for (int i = 0; i < ts.length; i++) { + if (i > 0) + sbuf.append(", "); + + sbuf.append(ts[i]); + } + } + + /** + * Returns the type name in the JVM internal style. + * For example, if the type is a nested class {@code foo.Bar.Baz}, + * then {@code foo.Bar$Baz} is returned. + */ + public String jvmTypeName() { return toString(); } + } + + /** + * Primitive types. + */ + public static class BaseType extends Type { + char descriptor; + BaseType(char c) { descriptor = c; } + + /** + * Constructs a BaseType. + * + * @param typeName void, int, ... + */ + public BaseType(String typeName) { + this(Descriptor.of(typeName).charAt(0)); + } + + /** + * Returns the descriptor representing this primitive type. + * + * @see javassist.bytecode.Descriptor + */ + public char getDescriptor() { return descriptor; } + + /** + * Returns the CtClass representing this + * primitive type. + */ + public CtClass getCtlass() { + return Descriptor.toPrimitiveClass(descriptor); + } + + /** + * Returns the string representation. + */ + public String toString() { + return Descriptor.toClassName(Character.toString(descriptor)); + } + + void encode(StringBuffer sb) { + sb.append(descriptor); + } + } + + /** + * Class types, array types, and type variables. + * This class is also used for representing a field type. + */ + public static abstract class ObjectType extends Type { + /** + * Returns the encoded string representing the object type signature. + */ + public String encode() { + StringBuffer sb = new StringBuffer(); + encode(sb); + return sb.toString(); + } + } + + /** + * Class types. + */ + public static class ClassType extends ObjectType { + String name; + TypeArgument[] arguments; + + static ClassType make(String s, int b, int e, + TypeArgument[] targs, ClassType parent) { + if (parent == null) + return new ClassType(s, b, e, targs); + else + return new NestedClassType(s, b, e, targs, parent); + } + + ClassType(String signature, int begin, int end, TypeArgument[] targs) { + name = signature.substring(begin, end).replace('/', '.'); + arguments = targs; + } + + /** + * A class type representing java.lang.Object. + */ + public static ClassType OBJECT = new ClassType("java.lang.Object", null); + + /** + * Constructs a ClassType. It represents + * the name of a non-nested class. + * + * @param className a fully qualified class name. + * @param args type arguments or null. + */ + public ClassType(String className, TypeArgument[] args) { + name = className; + arguments = args; + } + + /** + * Constructs a ClassType. It represents + * the name of a non-nested class. + * + * @param className a fully qualified class name. + */ + public ClassType(String className) { + this(className, null); + } + + /** + * Returns the class name. + */ + public String getName() { + return name; + } + + /** + * Returns the type arguments. + * + * @return null if no type arguments are given to this class. + */ + public TypeArgument[] getTypeArguments() { return arguments; } + + /** + * If this class is a member of another class, returns the + * class in which this class is declared. + * + * @return null if this class is not a member of another class. + */ + public ClassType getDeclaringClass() { return null; } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(); + ClassType parent = getDeclaringClass(); + if (parent != null) + sbuf.append(parent.toString()).append('.'); + + return toString2(sbuf); + } + + private String toString2(StringBuffer sbuf) { + sbuf.append(name); + if (arguments != null) { + sbuf.append('<'); + int n = arguments.length; + for (int i = 0; i < n; i++) { + if (i > 0) + sbuf.append(", "); + + sbuf.append(arguments[i].toString()); + } + + sbuf.append('>'); + } + + return sbuf.toString(); + } + + /** + * Returns the type name in the JVM internal style. + * For example, if the type is a nested class {@code foo.Bar.Baz}, + * then {@code foo.Bar$Baz} is returned. + */ + public String jvmTypeName() { + StringBuffer sbuf = new StringBuffer(); + ClassType parent = getDeclaringClass(); + if (parent != null) + sbuf.append(parent.jvmTypeName()).append('$'); + + return toString2(sbuf); + } + + void encode(StringBuffer sb) { + sb.append('L'); + encode2(sb); + sb.append(';'); + } + + void encode2(StringBuffer sb) { + ClassType parent = getDeclaringClass(); + if (parent != null) { + parent.encode2(sb); + sb.append('$'); + } + + sb.append(name.replace('.', '/')); + if (arguments != null) + TypeArgument.encode(sb, arguments); + } + } + + /** + * Nested class types. + */ + public static class NestedClassType extends ClassType { + ClassType parent; + NestedClassType(String s, int b, int e, + TypeArgument[] targs, ClassType p) { + super(s, b, e, targs); + parent = p; + } + + /** + * Constructs a NestedClassType. + * + * @param parent the class surrounding this class type. + * @param className a simple class name. It does not include + * a package name or a parent's class name. + * @param args type parameters or null. + */ + public NestedClassType(ClassType parent, String className, TypeArgument[] args) { + super(className, args); + this.parent = parent; + } + + /** + * Returns the class that declares this nested class. + * This nested class is a member of that declaring class. + */ + public ClassType getDeclaringClass() { return parent; } + } + + /** + * Array types. + */ + public static class ArrayType extends ObjectType { + int dim; + Type componentType; + + /** + * Constructs an ArrayType. + * + * @param d dimension. + * @param comp the component type. + */ + public ArrayType(int d, Type comp) { + dim = d; + componentType = comp; + } + + /** + * Returns the dimension of the array. + */ + public int getDimension() { return dim; } + + /** + * Returns the component type. + */ + public Type getComponentType() { + return componentType; + } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(componentType.toString()); + for (int i = 0; i < dim; i++) + sbuf.append("[]"); + + return sbuf.toString(); + } + + void encode(StringBuffer sb) { + for (int i = 0; i < dim; i++) + sb.append('['); + + componentType.encode(sb); + } + } + + /** + * Type variables. + */ + public static class TypeVariable extends ObjectType { + String name; + + TypeVariable(String sig, int begin, int end) { + name = sig.substring(begin, end); + } + + /** + * Constructs a TypeVariable. + * + * @param name the name of a type variable. + */ + public TypeVariable(String name) { + this.name = name; + } + + /** + * Returns the variable name. + */ + public String getName() { + return name; + } + + /** + * Returns the string representation. + */ + public String toString() { + return name; + } + + void encode(StringBuffer sb) { + sb.append('T').append(name).append(';'); + } + } + + /** + * Parses the given signature string as a class signature. + * + * @param sig the signature obtained from the SignatureAttribute + * of a ClassFile. + * @return a tree-like data structure representing a class signature. It provides + * convenient accessor methods. + * @throws BadBytecode thrown when a syntactical error is found. + * @see #getSignature() + * @since 3.5 + */ + public static ClassSignature toClassSignature(String sig) throws BadBytecode { + try { + return parseSig(sig); + } + catch (IndexOutOfBoundsException e) { + throw error(sig); + } + } + + /** + * Parses the given signature string as a method type signature. + * + * @param sig the signature obtained from the SignatureAttribute + * of a MethodInfo. + * @return @return a tree-like data structure representing a method signature. It provides + * convenient accessor methods. + * @throws BadBytecode thrown when a syntactical error is found. + * @see #getSignature() + * @since 3.5 + */ + public static MethodSignature toMethodSignature(String sig) throws BadBytecode { + try { + return parseMethodSig(sig); + } + catch (IndexOutOfBoundsException e) { + throw error(sig); + } + } + + /** + * Parses the given signature string as a field type signature. + * + * @param sig the signature string obtained from the SignatureAttribute + * of a FieldInfo. + * @return the field type signature. + * @throws BadBytecode thrown when a syntactical error is found. + * @see #getSignature() + * @since 3.5 + */ + public static ObjectType toFieldSignature(String sig) throws BadBytecode { + try { + return parseObjectType(sig, new Cursor(), false); + } + catch (IndexOutOfBoundsException e) { + throw error(sig); + } + } + + /** + * Parses the given signature string as a type signature. + * The type signature is either the field type signature or a base type + * descriptor including void type. + * + * @throws BadBytecode thrown when a syntactical error is found. + * @since 3.18 + */ + public static Type toTypeSignature(String sig) throws BadBytecode { + try { + return parseType(sig, new Cursor()); + } + catch (IndexOutOfBoundsException e) { + throw error(sig); + } + } + + private static ClassSignature parseSig(String sig) + throws BadBytecode, IndexOutOfBoundsException + { + Cursor cur = new Cursor(); + TypeParameter[] tp = parseTypeParams(sig, cur); + ClassType superClass = parseClassType(sig, cur); + int sigLen = sig.length(); + ArrayList ifArray = new ArrayList(); + while (cur.position < sigLen && sig.charAt(cur.position) == 'L') + ifArray.add(parseClassType(sig, cur)); + + ClassType[] ifs + = (ClassType[])ifArray.toArray(new ClassType[ifArray.size()]); + return new ClassSignature(tp, superClass, ifs); + } + + private static MethodSignature parseMethodSig(String sig) + throws BadBytecode + { + Cursor cur = new Cursor(); + TypeParameter[] tp = parseTypeParams(sig, cur); + if (sig.charAt(cur.position++) != '(') + throw error(sig); + + ArrayList params = new ArrayList(); + while (sig.charAt(cur.position) != ')') { + Type t = parseType(sig, cur); + params.add(t); + } + + cur.position++; + Type ret = parseType(sig, cur); + int sigLen = sig.length(); + ArrayList exceptions = new ArrayList(); + while (cur.position < sigLen && sig.charAt(cur.position) == '^') { + cur.position++; + ObjectType t = parseObjectType(sig, cur, false); + if (t instanceof ArrayType) + throw error(sig); + + exceptions.add(t); + } + + Type[] p = (Type[])params.toArray(new Type[params.size()]); + ObjectType[] ex = (ObjectType[])exceptions.toArray(new ObjectType[exceptions.size()]); + return new MethodSignature(tp, p, ret, ex); + } + + private static TypeParameter[] parseTypeParams(String sig, Cursor cur) + throws BadBytecode + { + ArrayList typeParam = new ArrayList(); + if (sig.charAt(cur.position) == '<') { + cur.position++; + while (sig.charAt(cur.position) != '>') { + int nameBegin = cur.position; + int nameEnd = cur.indexOf(sig, ':'); + ObjectType classBound = parseObjectType(sig, cur, true); + ArrayList ifBound = new ArrayList(); + while (sig.charAt(cur.position) == ':') { + cur.position++; + ObjectType t = parseObjectType(sig, cur, false); + ifBound.add(t); + } + + TypeParameter p = new TypeParameter(sig, nameBegin, nameEnd, + classBound, (ObjectType[])ifBound.toArray(new ObjectType[ifBound.size()])); + typeParam.add(p); + } + + cur.position++; + } + + return (TypeParameter[])typeParam.toArray(new TypeParameter[typeParam.size()]); + } + + private static ObjectType parseObjectType(String sig, Cursor c, boolean dontThrow) + throws BadBytecode + { + int i; + int begin = c.position; + switch (sig.charAt(begin)) { + case 'L' : + return parseClassType2(sig, c, null); + case 'T' : + i = c.indexOf(sig, ';'); + return new TypeVariable(sig, begin + 1, i); + case '[' : + return parseArray(sig, c); + default : + if (dontThrow) + return null; + else + throw error(sig); + } + } + + private static ClassType parseClassType(String sig, Cursor c) + throws BadBytecode + { + if (sig.charAt(c.position) == 'L') + return parseClassType2(sig, c, null); + else + throw error(sig); + } + + private static ClassType parseClassType2(String sig, Cursor c, ClassType parent) + throws BadBytecode + { + int start = ++c.position; + char t; + do { + t = sig.charAt(c.position++); + } while (t != '$' && t != '<' && t != ';'); + int end = c.position - 1; + TypeArgument[] targs; + if (t == '<') { + targs = parseTypeArgs(sig, c); + t = sig.charAt(c.position++); + } + else + targs = null; + + ClassType thisClass = ClassType.make(sig, start, end, targs, parent); + if (t == '$' || t == '.') { + c.position--; + return parseClassType2(sig, c, thisClass); + } + else + return thisClass; + } + + private static TypeArgument[] parseTypeArgs(String sig, Cursor c) throws BadBytecode { + ArrayList args = new ArrayList(); + char t; + while ((t = sig.charAt(c.position++)) != '>') { + TypeArgument ta; + if (t == '*' ) + ta = new TypeArgument(null, '*'); + else { + if (t != '+' && t != '-') { + t = ' '; + c.position--; + } + + ta = new TypeArgument(parseObjectType(sig, c, false), t); + } + + args.add(ta); + } + + return (TypeArgument[])args.toArray(new TypeArgument[args.size()]); + } + + private static ObjectType parseArray(String sig, Cursor c) throws BadBytecode { + int dim = 1; + while (sig.charAt(++c.position) == '[') + dim++; + + return new ArrayType(dim, parseType(sig, c)); + } + + private static Type parseType(String sig, Cursor c) throws BadBytecode { + Type t = parseObjectType(sig, c, true); + if (t == null) + t = new BaseType(sig.charAt(c.position++)); + + return t; + } + + private static BadBytecode error(String sig) { + return new BadBytecode("bad signature: " + sig); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java new file mode 100644 index 0000000..3f16225 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java @@ -0,0 +1,71 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +/** + * SourceFile_attribute. + */ +public class SourceFileAttribute extends AttributeInfo { + /** + * The name of this attribute "SourceFile". + */ + public static final String tag = "SourceFile"; + + SourceFileAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Constructs a SourceFile attribute. + * + * @param cp a constant pool table. + * @param filename the name of the source file. + */ + public SourceFileAttribute(ConstPool cp, String filename) { + super(cp, tag); + int index = cp.addUtf8Info(filename); + byte[] bvalue = new byte[2]; + bvalue[0] = (byte)(index >>> 8); + bvalue[1] = (byte)index; + set(bvalue); + } + + /** + * Returns the file name indicated by sourcefile_index. + */ + public String getFileName() { + return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0)); + } + + /** + * Makes a copy. Class names are replaced according to the + * given Map object. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames pairs of replaced and substituted + * class names. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + return new SourceFileAttribute(newCp, getFileName()); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMap.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMap.java new file mode 100644 index 0000000..541d6cd --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMap.java @@ -0,0 +1,576 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.bytecode.StackMapTable.InsertLocal; +import com.wenshuo.agent.javassist.bytecode.StackMapTable.NewRemover; +import com.wenshuo.agent.javassist.bytecode.StackMapTable.Shifter; + +/** + * Another stack_map attribute defined in CLDC 1.1 for J2ME. + * + *

This is an entry in the attributes table of a Code attribute. + * It was introduced by J2ME CLDC 1.1 (JSR 139) for pre-verification. + * + *

According to the CLDC specification, the sizes of some fields are not 16bit + * but 32bit if the code size is more than 64K or the number of the local variables + * is more than 64K. However, for the J2ME CLDC technology, they are always 16bit. + * The implementation of the StackMap class assumes they are 16bit. + * + * @see MethodInfo#doPreverify + * @see StackMapTable + * @since 3.12 + */ +public class StackMap extends AttributeInfo { + /** + * The name of this attribute "StackMap". + */ + public static final String tag = "StackMap"; + + + /** + * Constructs a stack_map attribute. + */ + StackMap(ConstPool cp, byte[] newInfo) { + super(cp, tag, newInfo); + } + + StackMap(ConstPool cp, int name_id, DataInputStream in) + throws IOException + { + super(cp, name_id, in); + } + + /** + * Returns number_of_entries. + */ + public int numOfEntries() { + return ByteArray.readU16bit(info, 0); + } + + /** + * Top_variable_info.tag. + */ + public static final int TOP = 0; + + /** + * Integer_variable_info.tag. + */ + public static final int INTEGER = 1; + + /** + * Float_variable_info.tag. + */ + public static final int FLOAT = 2; + + /** + * Double_variable_info.tag. + */ + public static final int DOUBLE = 3; + + /** + * Long_variable_info.tag. + */ + public static final int LONG = 4; + + /** + * Null_variable_info.tag. + */ + public static final int NULL = 5; + + /** + * UninitializedThis_variable_info.tag. + */ + public static final int THIS = 6; + + /** + * Object_variable_info.tag. + */ + public static final int OBJECT = 7; + + /** + * Uninitialized_variable_info.tag. + */ + public static final int UNINIT = 8; + + /** + * Makes a copy. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + Copier copier = new Copier(this, newCp, classnames); + copier.visit(); + return copier.getStackMap(); + } + + /** + * A code walker for a StackMap attribute. + */ + public static class Walker { + byte[] info; + + /** + * Constructs a walker. + */ + public Walker(StackMap sm) { + info = sm.get(); + } + + /** + * Visits each entry of the stack map frames. + */ + public void visit() { + int num = ByteArray.readU16bit(info, 0); + int pos = 2; + for (int i = 0; i < num; i++) { + int offset = ByteArray.readU16bit(info, pos); + int numLoc = ByteArray.readU16bit(info, pos + 2); + pos = locals(pos + 4, offset, numLoc); + int numStack = ByteArray.readU16bit(info, pos); + pos = stack(pos + 2, offset, numStack); + } + } + + /** + * Invoked when locals of stack_map_frame + * is visited. + */ + public int locals(int pos, int offset, int num) { + return typeInfoArray(pos, offset, num, true); + } + + /** + * Invoked when stack of stack_map_frame + * is visited. + */ + public int stack(int pos, int offset, int num) { + return typeInfoArray(pos, offset, num, false); + } + + /** + * Invoked when an array of verification_type_info is + * visited. + * + * @param num the number of elements. + * @param isLocals true if this array is for locals. + * false if it is for stack. + */ + public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { + for (int k = 0; k < num; k++) + pos = typeInfoArray2(k, pos); + + return pos; + } + + int typeInfoArray2(int k, int pos) { + byte tag = info[pos]; + if (tag == OBJECT) { + int clazz = ByteArray.readU16bit(info, pos + 1); + objectVariable(pos, clazz); + pos += 3; + } + else if (tag == UNINIT) { + int offsetOfNew = ByteArray.readU16bit(info, pos + 1); + uninitialized(pos, offsetOfNew); + pos += 3; + } + else { + typeInfo(pos, tag); + pos++; + } + + return pos; + } + + /** + * Invoked when an element of verification_type_info + * (except Object_variable_info and + * Uninitialized_variable_info) is visited. + */ + public void typeInfo(int pos, byte tag) {} + + /** + * Invoked when an element of type Object_variable_info + * is visited. + */ + public void objectVariable(int pos, int clazz) {} + + /** + * Invoked when an element of type Uninitialized_variable_info + * is visited. + */ + public void uninitialized(int pos, int offset) {} + } + + static class Copier extends Walker { + byte[] dest; + ConstPool srcCp, destCp; + Map classnames; + + Copier(StackMap map, ConstPool newCp, Map classnames) { + super(map); + srcCp = map.getConstPool(); + dest = new byte[info.length]; + destCp = newCp; + this.classnames = classnames; + } + + public void visit() { + int num = ByteArray.readU16bit(info, 0); + ByteArray.write16bit(num, dest, 0); + super.visit(); + } + + public int locals(int pos, int offset, int num) { + ByteArray.write16bit(offset, dest, pos - 4); + return super.locals(pos, offset, num); + } + + public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { + ByteArray.write16bit(num, dest, pos - 2); + return super.typeInfoArray(pos, offset, num, isLocals); + } + + public void typeInfo(int pos, byte tag) { + dest[pos] = tag; + } + + public void objectVariable(int pos, int clazz) { + dest[pos] = OBJECT; + int newClazz = srcCp.copy(clazz, destCp, classnames); + ByteArray.write16bit(newClazz, dest, pos + 1); + } + + public void uninitialized(int pos, int offset) { + dest[pos] = UNINIT; + ByteArray.write16bit(offset, dest, pos + 1); + } + + public StackMap getStackMap() { + return new StackMap(destCp, dest); + } + } + + /** + * Updates this stack map table when a new local variable is inserted + * for a new parameter. + * + * @param index the index of the added local variable. + * @param tag the type tag of that local variable. + * It is available by StackMapTable.typeTagOf(char). + * @param classInfo the index of the CONSTANT_Class_info structure + * in a constant pool table. This should be zero unless the tag + * is ITEM_Object. + * + * @see javassist.CtBehavior#addParameter(javassist.CtClass) + * @see StackMapTable#typeTagOf(char) + * @see ConstPool + */ + public void insertLocal(int index, int tag, int classInfo) + throws BadBytecode + { + byte[] data = new InsertLocal(this, index, tag, classInfo).doit(); + this.set(data); + } + + static class SimpleCopy extends Walker { + Writer writer; + + SimpleCopy(StackMap map) { + super(map); + writer = new Writer(); + } + + byte[] doit() { + visit(); + return writer.toByteArray(); + } + + public void visit() { + int num = ByteArray.readU16bit(info, 0); + writer.write16bit(num); + super.visit(); + } + + public int locals(int pos, int offset, int num) { + writer.write16bit(offset); + return super.locals(pos, offset, num); + } + + public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { + writer.write16bit(num); + return super.typeInfoArray(pos, offset, num, isLocals); + } + + public void typeInfo(int pos, byte tag) { + writer.writeVerifyTypeInfo(tag, 0); + } + + public void objectVariable(int pos, int clazz) { + writer.writeVerifyTypeInfo(OBJECT, clazz); + } + + public void uninitialized(int pos, int offset) { + writer.writeVerifyTypeInfo(UNINIT, offset); + } + } + + static class InsertLocal extends SimpleCopy { + private int varIndex; + private int varTag, varData; + + InsertLocal(StackMap map, int varIndex, int varTag, int varData) { + super(map); + this.varIndex = varIndex; + this.varTag = varTag; + this.varData = varData; + } + + public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { + if (!isLocals || num < varIndex) + return super.typeInfoArray(pos, offset, num, isLocals); + + writer.write16bit(num + 1); + for (int k = 0; k < num; k++) { + if (k == varIndex) + writeVarTypeInfo(); + + pos = typeInfoArray2(k, pos); + } + + if (num == varIndex) + writeVarTypeInfo(); + + return pos; + } + + private void writeVarTypeInfo() { + if (varTag == OBJECT) + writer.writeVerifyTypeInfo(OBJECT, varData); + else if (varTag == UNINIT) + writer.writeVerifyTypeInfo(UNINIT, varData); + else + writer.writeVerifyTypeInfo(varTag, 0); + } + } + + void shiftPc(int where, int gapSize, boolean exclusive) + throws BadBytecode + { + new Shifter(this, where, gapSize, exclusive).visit(); + } + + static class Shifter extends Walker { + private int where, gap; + private boolean exclusive; + + public Shifter(StackMap smt, int where, int gap, boolean exclusive) { + super(smt); + this.where = where; + this.gap = gap; + this.exclusive = exclusive; + } + + public int locals(int pos, int offset, int num) { + if (exclusive ? where <= offset : where < offset) + ByteArray.write16bit(offset + gap, info, pos - 4); + + return super.locals(pos, offset, num); + } + + public void uninitialized(int pos, int offset) { + if (where <= offset) + ByteArray.write16bit(offset + gap, info, pos + 1); + } + } + + /** + * @see CodeIterator.Switcher#adjustOffsets(int, int) + */ + void shiftForSwitch(int where, int gapSize) throws BadBytecode { + new SwitchShifter(this, where, gapSize).visit(); + } + + static class SwitchShifter extends Walker { + private int where, gap; + + public SwitchShifter(StackMap smt, int where, int gap) { + super(smt); + this.where = where; + this.gap = gap; + } + + public int locals(int pos, int offset, int num) { + if (where == pos + offset) + ByteArray.write16bit(offset - gap, info, pos - 4); + else if (where == pos) + ByteArray.write16bit(offset + gap, info, pos - 4); + + return super.locals(pos, offset, num); + } + } + + /** + * Undocumented method. Do not use; internal-use only. + * + *

This method is for javassist.convert.TransformNew. + * It is called to update the stack map when + * the NEW opcode (and the following DUP) is removed. + * + * @param where the position of the removed NEW opcode. + */ + public void removeNew(int where) throws CannotCompileException { + byte[] data = new NewRemover(this, where).doit(); + this.set(data); + } + + static class NewRemover extends SimpleCopy { + int posOfNew; + + NewRemover(StackMap map, int where) { + super(map); + posOfNew = where; + } + + public int stack(int pos, int offset, int num) { + return stackTypeInfoArray(pos, offset, num); + } + + private int stackTypeInfoArray(int pos, int offset, int num) { + int p = pos; + int count = 0; + for (int k = 0; k < num; k++) { + byte tag = info[p]; + if (tag == OBJECT) + p += 3; + else if (tag == UNINIT) { + int offsetOfNew = ByteArray.readU16bit(info, p + 1); + if (offsetOfNew == posOfNew) + count++; + + p += 3; + } + else + p++; + } + + writer.write16bit(num - count); + for (int k = 0; k < num; k++) { + byte tag = info[pos]; + if (tag == OBJECT) { + int clazz = ByteArray.readU16bit(info, pos + 1); + objectVariable(pos, clazz); + pos += 3; + } + else if (tag == UNINIT) { + int offsetOfNew = ByteArray.readU16bit(info, pos + 1); + if (offsetOfNew != posOfNew) + uninitialized(pos, offsetOfNew); + + pos += 3; + } + else { + typeInfo(pos, tag); + pos++; + } + } + + return pos; + } + } + + /** + * Prints this stack map. + */ + public void print(java.io.PrintWriter out) { + new Printer(this, out).print(); + } + + static class Printer extends Walker { + private java.io.PrintWriter writer; + + public Printer(StackMap map, java.io.PrintWriter out) { + super(map); + writer = out; + } + + public void print() { + int num = ByteArray.readU16bit(info, 0); + writer.println(num + " entries"); + visit(); + } + + public int locals(int pos, int offset, int num) { + writer.println(" * offset " + offset); + return super.locals(pos, offset, num); + } + } + + /** + * Internal use only. + */ + public static class Writer { + // see javassist.bytecode.stackmap.MapMaker + + private ByteArrayOutputStream output; + + /** + * Constructs a writer. + */ + public Writer() { + output = new ByteArrayOutputStream(); + } + + /** + * Converts the written data into a byte array. + */ + public byte[] toByteArray() { + return output.toByteArray(); + } + + /** + * Converts to a StackMap attribute. + */ + public StackMap toStackMap(ConstPool cp) { + return new StackMap(cp, output.toByteArray()); + } + + /** + * Writes a union verification_type_info value. + * + * @param data cpool_index or offset. + */ + public void writeVerifyTypeInfo(int tag, int data) { + output.write(tag); + if (tag == StackMap.OBJECT || tag == StackMap.UNINIT) + write16bit(data); + } + + /** + * Writes a 16bit value. + */ + public void write16bit(int value) { + output.write((value >>> 8) & 0xff); + output.write(value & 0xff); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMapTable.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMapTable.java new file mode 100644 index 0000000..5a843fe --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMapTable.java @@ -0,0 +1,1049 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.io.IOException; +import java.util.Map; +import com.wenshuo.agent.javassist.CannotCompileException; + +/** + * stack_map attribute. + * + *

This is an entry in the attributes table of a Code attribute. + * It was introduced by J2SE 6 for the verification by + * typechecking. + * + * @see StackMap + * @since 3.4 + */ +public class StackMapTable extends AttributeInfo { + /** + * The name of this attribute "StackMapTable". + */ + public static final String tag = "StackMapTable"; + + /** + * Constructs a stack_map attribute. + */ + StackMapTable(ConstPool cp, byte[] newInfo) { + super(cp, tag, newInfo); + } + + StackMapTable(ConstPool cp, int name_id, DataInputStream in) + throws IOException + { + super(cp, name_id, in); + } + + /** + * Makes a copy. + * + * @exception RuntimeCopyException if a BadBytecode + * exception is thrown while copying, + * it is converted into + * RuntimeCopyException. + * + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) + throws RuntimeCopyException + { + try { + return new StackMapTable(newCp, + new Copier(this.constPool, info, newCp, classnames).doit()); + } + catch (BadBytecode e) { + throw new RuntimeCopyException("bad bytecode. fatal?"); + } + } + + /** + * An exception that may be thrown by copy() + * in StackMapTable. + */ + public static class RuntimeCopyException extends RuntimeException { + /** + * Constructs an exception. + */ + public RuntimeCopyException(String s) { + super(s); + } + } + + void write(DataOutputStream out) throws IOException { + super.write(out); + } + + /** + * Top_variable_info.tag. + */ + public static final int TOP = 0; + + /** + * Integer_variable_info.tag. + */ + public static final int INTEGER = 1; + + /** + * Float_variable_info.tag. + */ + public static final int FLOAT = 2; + + /** + * Double_variable_info.tag. + */ + public static final int DOUBLE = 3; + + /** + * Long_variable_info.tag. + */ + public static final int LONG = 4; + + /** + * Null_variable_info.tag. + */ + public static final int NULL = 5; + + /** + * UninitializedThis_variable_info.tag. + */ + public static final int THIS = 6; + + /** + * Object_variable_info.tag. + */ + public static final int OBJECT = 7; + + /** + * Uninitialized_variable_info.tag. + */ + public static final int UNINIT = 8; + + /** + * A code walker for a StackMapTable attribute. + */ + public static class Walker { + byte[] info; + int numOfEntries; + + /** + * Constructs a walker. + * + * @param smt the StackMapTable that this walker + * walks around. + */ + public Walker(StackMapTable smt) { + this(smt.get()); + } + + /** + * Constructs a walker. + * + * @param data the info field of the + * attribute_info structure. + * It can be obtained by get() + * in the AttributeInfo class. + */ + public Walker(byte[] data) { + info = data; + numOfEntries = ByteArray.readU16bit(data, 0); + } + + /** + * Returns the number of the entries. + */ + public final int size() { return numOfEntries; } + + /** + * Visits each entry of the stack map frames. + */ + public void parse() throws BadBytecode { + int n = numOfEntries; + int pos = 2; + for (int i = 0; i < n; i++) + pos = stackMapFrames(pos, i); + } + + /** + * Invoked when the next entry of the stack map frames is visited. + * + * @param pos the position of the frame in the info + * field of attribute_info structure. + * @param nth the frame is the N-th + * (0, 1st, 2nd, 3rd, 4th, ...) entry. + * @return the position of the next frame. + */ + int stackMapFrames(int pos, int nth) throws BadBytecode { + int type = info[pos] & 0xff; + if (type < 64) { + sameFrame(pos, type); + pos++; + } + else if (type < 128) + pos = sameLocals(pos, type); + else if (type < 247) + throw new BadBytecode("bad frame_type in StackMapTable"); + else if (type == 247) // SAME_LOCALS_1_STACK_ITEM_EXTENDED + pos = sameLocals(pos, type); + else if (type < 251) { + int offset = ByteArray.readU16bit(info, pos + 1); + chopFrame(pos, offset, 251 - type); + pos += 3; + } + else if (type == 251) { // SAME_FRAME_EXTENDED + int offset = ByteArray.readU16bit(info, pos + 1); + sameFrame(pos, offset); + pos += 3; + } + else if (type < 255) + pos = appendFrame(pos, type); + else // FULL_FRAME + pos = fullFrame(pos); + + return pos; + } + + /** + * Invoked if the visited frame is a same_frame or + * a same_frame_extended. + * + * @param pos the position of this frame in the info + * field of attribute_info structure. + * @param offsetDelta + */ + public void sameFrame(int pos, int offsetDelta) throws BadBytecode {} + + private int sameLocals(int pos, int type) throws BadBytecode { + int top = pos; + int offset; + if (type < 128) + offset = type - 64; + else { // type == 247 + offset = ByteArray.readU16bit(info, pos + 1); + pos += 2; + } + + int tag = info[pos + 1] & 0xff; + int data = 0; + if (tag == OBJECT || tag == UNINIT) { + data = ByteArray.readU16bit(info, pos + 2); + objectOrUninitialized(tag, data, pos + 2); + pos += 2; + } + + sameLocals(top, offset, tag, data); + return pos + 2; + } + + /** + * Invoked if the visited frame is a same_locals_1_stack_item_frame + * or a same_locals_1_stack_item_frame_extended. + * + * @param pos the position. + * @param offsetDelta + * @param stackTag stack[0].tag. + * @param stackData stack[0].cpool_index + * if the tag is OBJECT, + * or stack[0].offset + * if the tag is UNINIT. + */ + public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) + throws BadBytecode {} + + /** + * Invoked if the visited frame is a chop_frame. + * + * @param pos the position. + * @param offsetDelta + * @param k the k last locals are absent. + */ + public void chopFrame(int pos, int offsetDelta, int k) throws BadBytecode {} + + private int appendFrame(int pos, int type) throws BadBytecode { + int k = type - 251; + int offset = ByteArray.readU16bit(info, pos + 1); + int[] tags = new int[k]; + int[] data = new int[k]; + int p = pos + 3; + for (int i = 0; i < k; i++) { + int tag = info[p] & 0xff; + tags[i] = tag; + if (tag == OBJECT || tag == UNINIT) { + data[i] = ByteArray.readU16bit(info, p + 1); + objectOrUninitialized(tag, data[i], p + 1); + p += 3; + } + else { + data[i] = 0; + p++; + } + } + + appendFrame(pos, offset, tags, data); + return p; + } + + /** + * Invoked if the visited frame is a append_frame. + * + * @param pos the position. + * @param offsetDelta + * @param tags locals[i].tag. + * @param data locals[i].cpool_index + * or locals[i].offset. + */ + public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) + throws BadBytecode {} + + private int fullFrame(int pos) throws BadBytecode { + int offset = ByteArray.readU16bit(info, pos + 1); + int numOfLocals = ByteArray.readU16bit(info, pos + 3); + int[] localsTags = new int[numOfLocals]; + int[] localsData = new int[numOfLocals]; + int p = verifyTypeInfo(pos + 5, numOfLocals, localsTags, localsData); + int numOfItems = ByteArray.readU16bit(info, p); + int[] itemsTags = new int[numOfItems]; + int[] itemsData = new int[numOfItems]; + p = verifyTypeInfo(p + 2, numOfItems, itemsTags, itemsData); + fullFrame(pos, offset, localsTags, localsData, itemsTags, itemsData); + return p; + } + + /** + * Invoked if the visited frame is full_frame. + * + * @param pos the position. + * @param offsetDelta + * @param localTags locals[i].tag + * @param localData locals[i].cpool_index + * or locals[i].offset + * @param stackTags stack[i].tag + * @param stackData stack[i].cpool_index + * or stack[i].offset + */ + public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) + throws BadBytecode {} + + private int verifyTypeInfo(int pos, int n, int[] tags, int[] data) { + for (int i = 0; i < n; i++) { + int tag = info[pos++] & 0xff; + tags[i] = tag; + if (tag == OBJECT || tag == UNINIT) { + data[i] = ByteArray.readU16bit(info, pos); + objectOrUninitialized(tag, data[i], pos); + pos += 2; + } + } + + return pos; + } + + /** + * Invoked if Object_variable_info + * or Uninitialized_variable_info is visited. + * + * @param tag OBJECT or UNINIT. + * @param data the value of cpool_index or offset. + * @param pos the position of cpool_index or offset. + */ + public void objectOrUninitialized(int tag, int data, int pos) {} + } + + static class SimpleCopy extends Walker { + private Writer writer; + + public SimpleCopy(byte[] data) { + super(data); + writer = new Writer(data.length); + } + + public byte[] doit() throws BadBytecode { + parse(); + return writer.toByteArray(); + } + + public void sameFrame(int pos, int offsetDelta) { + writer.sameFrame(offsetDelta); + } + + public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { + writer.sameLocals(offsetDelta, stackTag, copyData(stackTag, stackData)); + } + + public void chopFrame(int pos, int offsetDelta, int k) { + writer.chopFrame(offsetDelta, k); + } + + public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { + writer.appendFrame(offsetDelta, tags, copyData(tags, data)); + } + + public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) { + writer.fullFrame(offsetDelta, localTags, copyData(localTags, localData), + stackTags, copyData(stackTags, stackData)); + } + + protected int copyData(int tag, int data) { + return data; + } + + protected int[] copyData(int[] tags, int[] data) { + return data; + } + } + + static class Copier extends SimpleCopy { + private ConstPool srcPool, destPool; + private Map classnames; + + public Copier(ConstPool src, byte[] data, ConstPool dest, Map names) { + super(data); + srcPool = src; + destPool = dest; + classnames = names; + } + + protected int copyData(int tag, int data) { + if (tag == OBJECT) + return srcPool.copy(data, destPool, classnames); + else + return data; + } + + protected int[] copyData(int[] tags, int[] data) { + int[] newData = new int[data.length]; + for (int i = 0; i < data.length; i++) + if (tags[i] == OBJECT) + newData[i] = srcPool.copy(data[i], destPool, classnames); + else + newData[i] = data[i]; + + return newData; + } + } + + /** + * Updates this stack map table when a new local variable is inserted + * for a new parameter. + * + * @param index the index of the added local variable. + * @param tag the type tag of that local variable. + * @param classInfo the index of the CONSTANT_Class_info structure + * in a constant pool table. This should be zero unless the tag + * is ITEM_Object. + * + * @see javassist.CtBehavior#addParameter(javassist.CtClass) + * @see #typeTagOf(char) + * @see ConstPool + */ + public void insertLocal(int index, int tag, int classInfo) + throws BadBytecode + { + byte[] data = new InsertLocal(this.get(), index, tag, classInfo).doit(); + this.set(data); + } + + /** + * Returns the tag of the type specified by the + * descriptor. This method returns INTEGER + * unless the descriptor is either D (double), F (float), + * J (long), L (class type), or [ (array). + * + * @param descriptor the type descriptor. + * @see Descriptor + */ + public static int typeTagOf(char descriptor) { + switch (descriptor) { + case 'D' : + return DOUBLE; + case 'F' : + return FLOAT; + case 'J' : + return LONG; + case 'L' : + case '[' : + return OBJECT; + // case 'V' : + default : + return INTEGER; + } + } + + /* This implementation assumes that a local variable initially + * holding a parameter value is never changed to be a different + * type. + * + */ + static class InsertLocal extends SimpleCopy { + private int varIndex; + private int varTag, varData; + + public InsertLocal(byte[] data, int varIndex, int varTag, int varData) { + super(data); + this.varIndex = varIndex; + this.varTag = varTag; + this.varData = varData; + } + + public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) { + int len = localTags.length; + if (len < varIndex) { + super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); + return; + } + + int typeSize = (varTag == LONG || varTag == DOUBLE) ? 2 : 1; + int[] localTags2 = new int[len + typeSize]; + int[] localData2 = new int[len + typeSize]; + int index = varIndex; + int j = 0; + for (int i = 0; i < len; i++) { + if (j == index) + j += typeSize; + + localTags2[j] = localTags[i]; + localData2[j++] = localData[i]; + } + + localTags2[index] = varTag; + localData2[index] = varData; + if (typeSize > 1) { + localTags2[index + 1] = TOP; + localData2[index + 1] = 0; + } + + super.fullFrame(pos, offsetDelta, localTags2, localData2, stackTags, stackData); + } + } + + /** + * A writer of stack map tables. + */ + public static class Writer { + ByteArrayOutputStream output; + int numOfEntries; + + /** + * Constructs a writer. + * @param size the initial buffer size. + */ + public Writer(int size) { + output = new ByteArrayOutputStream(size); + numOfEntries = 0; + output.write(0); // u2 number_of_entries + output.write(0); + } + + /** + * Returns the stack map table written out. + */ + public byte[] toByteArray() { + byte[] b = output.toByteArray(); + ByteArray.write16bit(numOfEntries, b, 0); + return b; + } + + /** + * Constructs and a return a stack map table containing + * the written stack map entries. + * + * @param cp the constant pool used to write + * the stack map entries. + */ + public StackMapTable toStackMapTable(ConstPool cp) { + return new StackMapTable(cp, toByteArray()); + } + + /** + * Writes a same_frame or a same_frame_extended. + */ + public void sameFrame(int offsetDelta) { + numOfEntries++; + if (offsetDelta < 64) + output.write(offsetDelta); + else { + output.write(251); // SAME_FRAME_EXTENDED + write16(offsetDelta); + } + } + + /** + * Writes a same_locals_1_stack_item + * or a same_locals_1_stack_item_extended. + * + * @param tag stack[0].tag. + * @param data stack[0].cpool_index + * if the tag is OBJECT, + * or stack[0].offset + * if the tag is UNINIT. + * Otherwise, this parameter is not used. + */ + public void sameLocals(int offsetDelta, int tag, int data) { + numOfEntries++; + if (offsetDelta < 64) + output.write(offsetDelta + 64); + else { + output.write(247); // SAME_LOCALS_1_STACK_ITEM_EXTENDED + write16(offsetDelta); + } + + writeTypeInfo(tag, data); + } + + /** + * Writes a chop_frame. + * + * @param k the number of absent locals. 1, 2, or 3. + */ + public void chopFrame(int offsetDelta, int k) { + numOfEntries++; + output.write(251 - k); + write16(offsetDelta); + } + + /** + * Writes a append_frame. The number of the appended + * locals is specified by the length of tags. + * + * @param tags locals[].tag. + * The length of this array must be + * either 1, 2, or 3. + * @param data locals[].cpool_index + * if the tag is OBJECT, + * or locals[].offset + * if the tag is UNINIT. + * Otherwise, this parameter is not used. + */ + public void appendFrame(int offsetDelta, int[] tags, int[] data) { + numOfEntries++; + int k = tags.length; // k is 1, 2, or 3 + output.write(k + 251); + write16(offsetDelta); + for (int i = 0; i < k; i++) + writeTypeInfo(tags[i], data[i]); + } + + /** + * Writes a full_frame. + * number_of_locals and number_of_stack_items + * are specified by the the length of localTags and + * stackTags. + * + * @param localTags locals[].tag. + * @param localData locals[].cpool_index + * if the tag is OBJECT, + * or locals[].offset + * if the tag is UNINIT. + * Otherwise, this parameter is not used. + * @param stackTags stack[].tag. + * @param stackData stack[].cpool_index + * if the tag is OBJECT, + * or stack[].offset + * if the tag is UNINIT. + * Otherwise, this parameter is not used. + */ + public void fullFrame(int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) { + numOfEntries++; + output.write(255); // FULL_FRAME + write16(offsetDelta); + int n = localTags.length; + write16(n); + for (int i = 0; i < n; i++) + writeTypeInfo(localTags[i], localData[i]); + + n = stackTags.length; + write16(n); + for (int i = 0; i < n; i++) + writeTypeInfo(stackTags[i], stackData[i]); + } + + private void writeTypeInfo(int tag, int data) { + output.write(tag); + if (tag == OBJECT || tag == UNINIT) + write16(data); + } + + private void write16(int value) { + output.write((value >>> 8) & 0xff); + output.write(value & 0xff); + } + } + + /** + * Prints the stack table map. + */ + public void println(PrintWriter w) { + Printer.print(this, w); + } + + /** + * Prints the stack table map. + * + * @param ps a print stream such as System.out. + */ + public void println(java.io.PrintStream ps) { + Printer.print(this, new java.io.PrintWriter(ps, true)); + } + + static class Printer extends Walker { + private PrintWriter writer; + private int offset; + + /** + * Prints the stack table map. + */ + public static void print(StackMapTable smt, PrintWriter writer) { + try { + new Printer(smt.get(), writer).parse(); + } + catch (BadBytecode e) { + writer.println(e.getMessage()); + } + } + + Printer(byte[] data, PrintWriter pw) { + super(data); + writer = pw; + offset = -1; + } + + public void sameFrame(int pos, int offsetDelta) { + offset += offsetDelta + 1; + writer.println(offset + " same frame: " + offsetDelta); + } + + public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { + offset += offsetDelta + 1; + writer.println(offset + " same locals: " + offsetDelta); + printTypeInfo(stackTag, stackData); + } + + public void chopFrame(int pos, int offsetDelta, int k) { + offset += offsetDelta + 1; + writer.println(offset + " chop frame: " + offsetDelta + ", " + k + " last locals"); + } + + public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { + offset += offsetDelta + 1; + writer.println(offset + " append frame: " + offsetDelta); + for (int i = 0; i < tags.length; i++) + printTypeInfo(tags[i], data[i]); + } + + public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) { + offset += offsetDelta + 1; + writer.println(offset + " full frame: " + offsetDelta); + writer.println("[locals]"); + for (int i = 0; i < localTags.length; i++) + printTypeInfo(localTags[i], localData[i]); + + writer.println("[stack]"); + for (int i = 0; i < stackTags.length; i++) + printTypeInfo(stackTags[i], stackData[i]); + } + + private void printTypeInfo(int tag, int data) { + String msg = null; + switch (tag) { + case TOP : + msg = "top"; + break; + case INTEGER : + msg = "integer"; + break; + case FLOAT : + msg = "float"; + break; + case DOUBLE : + msg = "double"; + break; + case LONG : + msg = "long"; + break; + case NULL : + msg = "null"; + break; + case THIS : + msg = "this"; + break; + case OBJECT : + msg = "object (cpool_index " + data + ")"; + break; + case UNINIT : + msg = "uninitialized (offset " + data + ")"; + break; + } + + writer.print(" "); + writer.println(msg); + } + } + + void shiftPc(int where, int gapSize, boolean exclusive) + throws BadBytecode + { + new OffsetShifter(this, where, gapSize).parse(); + new Shifter(this, where, gapSize, exclusive).doit(); + } + + static class OffsetShifter extends Walker { + int where, gap; + + public OffsetShifter(StackMapTable smt, int where, int gap) { + super(smt); + this.where = where; + this.gap = gap; + } + + public void objectOrUninitialized(int tag, int data, int pos) { + if (tag == UNINIT) + if (where <= data) + ByteArray.write16bit(data + gap, info, pos); + } + } + + static class Shifter extends Walker { + private StackMapTable stackMap; + int where, gap; + int position; + byte[] updatedInfo; + boolean exclusive; + + public Shifter(StackMapTable smt, int where, int gap, boolean exclusive) { + super(smt); + stackMap = smt; + this.where = where; + this.gap = gap; + this.position = 0; + this.updatedInfo = null; + this.exclusive = exclusive; + } + + public void doit() throws BadBytecode { + parse(); + if (updatedInfo != null) + stackMap.set(updatedInfo); + } + + public void sameFrame(int pos, int offsetDelta) { + update(pos, offsetDelta, 0, 251); + } + + public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { + update(pos, offsetDelta, 64, 247); + } + + void update(int pos, int offsetDelta, int base, int entry) { + int oldPos = position; + position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); + boolean match; + if (exclusive) + match = oldPos < where && where <= position; + else + match = oldPos <= where && where < position; + + if (match) { + int newDelta = offsetDelta + gap; + position += gap; + if (newDelta < 64) + info[pos] = (byte)(newDelta + base); + else if (offsetDelta < 64) { + byte[] newinfo = insertGap(info, pos, 2); + newinfo[pos] = (byte)entry; + ByteArray.write16bit(newDelta, newinfo, pos + 1); + updatedInfo = newinfo; + } + else + ByteArray.write16bit(newDelta, info, pos + 1); + } + } + + static byte[] insertGap(byte[] info, int where, int gap) { + int len = info.length; + byte[] newinfo = new byte[len + gap]; + for (int i = 0; i < len; i++) + newinfo[i + (i < where ? 0 : gap)] = info[i]; + + return newinfo; + } + + public void chopFrame(int pos, int offsetDelta, int k) { + update(pos, offsetDelta); + } + + public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { + update(pos, offsetDelta); + } + + public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) { + update(pos, offsetDelta); + } + + void update(int pos, int offsetDelta) { + int oldPos = position; + position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); + boolean match; + if (exclusive) + match = oldPos < where && where <= position; + else + match = oldPos <= where && where < position; + + if (match) { + int newDelta = offsetDelta + gap; + ByteArray.write16bit(newDelta, info, pos + 1); + position += gap; + } + } + } + + /** + * @see CodeIterator.Switcher#adjustOffsets(int, int) + */ + void shiftForSwitch(int where, int gapSize) throws BadBytecode { + new SwitchShifter(this, where, gapSize).doit(); + } + + static class SwitchShifter extends Shifter { + SwitchShifter(StackMapTable smt, int where, int gap) { + super(smt, where, gap, false); + } + + void update(int pos, int offsetDelta, int base, int entry) { + int oldPos = position; + position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); + int newDelta = offsetDelta; + if (where == position) + newDelta = offsetDelta - gap; + else if (where == oldPos) + newDelta = offsetDelta + gap; + else + return; + + if (offsetDelta < 64) + if (newDelta < 64) + info[pos] = (byte)(newDelta + base); + else { + byte[] newinfo = insertGap(info, pos, 2); + newinfo[pos] = (byte)entry; + ByteArray.write16bit(newDelta, newinfo, pos + 1); + updatedInfo = newinfo; + } + else + if (newDelta < 64) { + byte[] newinfo = deleteGap(info, pos, 2); + newinfo[pos] = (byte)(newDelta + base); + updatedInfo = newinfo; + } + else + ByteArray.write16bit(newDelta, info, pos + 1); + } + + static byte[] deleteGap(byte[] info, int where, int gap) { + where += gap; + int len = info.length; + byte[] newinfo = new byte[len - gap]; + for (int i = 0; i < len; i++) + newinfo[i - (i < where ? 0 : gap)] = info[i]; + + return newinfo; + } + + void update(int pos, int offsetDelta) { + int oldPos = position; + position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); + int newDelta = offsetDelta; + if (where == position) + newDelta = offsetDelta - gap; + else if (where == oldPos) + newDelta = offsetDelta + gap; + else + return; + + ByteArray.write16bit(newDelta, info, pos + 1); + } + } + + /** + * Undocumented method. Do not use; internal-use only. + * + *

This method is for javassist.convert.TransformNew. + * It is called to update the stack map table when + * the NEW opcode (and the following DUP) is removed. + * + * @param where the position of the removed NEW opcode. + */ + public void removeNew(int where) throws CannotCompileException { + try { + byte[] data = new NewRemover(this.get(), where).doit(); + this.set(data); + } + catch (BadBytecode e) { + throw new CannotCompileException("bad stack map table", e); + } + } + + static class NewRemover extends SimpleCopy { + int posOfNew; + + public NewRemover(byte[] data, int pos) { + super(data); + posOfNew = pos; + } + + public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { + if (stackTag == UNINIT && stackData == posOfNew) + super.sameFrame(pos, offsetDelta); + else + super.sameLocals(pos, offsetDelta, stackTag, stackData); + } + + public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) { + int n = stackTags.length - 1; + for (int i = 0; i < n; i++) + if (stackTags[i] == UNINIT && stackData[i] == posOfNew + && stackTags[i + 1] == UNINIT && stackData[i + 1] == posOfNew) { + n++; + int[] stackTags2 = new int[n - 2]; + int[] stackData2 = new int[n - 2]; + int k = 0; + for (int j = 0; j < n; j++) + if (j == i) + j++; + else { + stackTags2[k] = stackTags[j]; + stackData2[k++] = stackData[j]; + } + + stackTags = stackTags2; + stackData = stackData2; + break; + } + + super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java new file mode 100644 index 0000000..52484b7 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java @@ -0,0 +1,56 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +/** + * Synthetic_attribute. + */ +public class SyntheticAttribute extends AttributeInfo { + /** + * The name of this attribute "Synthetic". + */ + public static final String tag = "Synthetic"; + + SyntheticAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Constructs a Synthetic attribute. + * + * @param cp a constant pool table. + */ + public SyntheticAttribute(ConstPool cp) { + super(cp, tag, new byte[0]); + } + + /** + * Makes a copy. + * + * @param newCp the constant pool table used by the new copy. + * @param classnames should be null. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + return new SyntheticAttribute(newCp); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java new file mode 100644 index 0000000..29f075f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java @@ -0,0 +1,360 @@ +package com.wenshuo.agent.javassist.bytecode; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import com.wenshuo.agent.javassist.bytecode.annotation.TypeAnnotationsWriter; + +/** + * A class representing + * {@code RuntimeVisibleTypeAnnotations} attribute and + * {@code RuntimeInvisibleTypeAnnotations} attribute. + * + * @since 3.19 + */ +public class TypeAnnotationsAttribute extends AttributeInfo { + /** + * The name of the {@code RuntimeVisibleTypeAnnotations} attribute. + */ + public static final String visibleTag = "RuntimeVisibleTypeAnnotations"; + + /** + * The name of the {@code RuntimeInvisibleTypeAnnotations} attribute. + */ + public static final String invisibleTag = "RuntimeInvisibleTypeAnnotations"; + + /** + * Constructs a Runtime(In)VisibleTypeAnnotations_attribute. + * + * @param cp constant pool + * @param attrname attribute name (visibleTag or + * invisibleTag). + * @param info the contents of this attribute. It does not + * include attribute_name_index or + * attribute_length. + */ + public TypeAnnotationsAttribute(ConstPool cp, String attrname, byte[] info) { + super(cp, attrname, info); + } + + /** + * @param n the attribute name. + */ + TypeAnnotationsAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Returns num_annotations. + */ + public int numAnnotations() { + return ByteArray.readU16bit(info, 0); + } + + /** + * Copies this attribute and returns a new copy. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + Copier copier = new Copier(info, constPool, newCp, classnames); + try { + copier.annotationArray(); + return new TypeAnnotationsAttribute(newCp, getName(), copier.close()); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * @param oldname a JVM class name. + * @param newname a JVM class name. + */ + void renameClass(String oldname, String newname) { + HashMap map = new HashMap(); + map.put(oldname, newname); + renameClass(map); + } + + void renameClass(Map classnames) { + Renamer renamer = new Renamer(info, getConstPool(), classnames); + try { + renamer.annotationArray(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + void getRefClasses(Map classnames) { renameClass(classnames); } + + /** + * To visit each elements of the type annotation attribute, + * call {@code annotationArray()}. + * + * @see #annotationArray() + */ + static class TAWalker extends AnnotationsAttribute.Walker { + SubWalker subWalker; + + TAWalker(byte[] attrInfo) { + super(attrInfo); + subWalker = new SubWalker(attrInfo); + } + + int annotationArray(int pos, int num) throws Exception { + for (int i = 0; i < num; i++) { + int targetType = info[pos] & 0xff; + pos = subWalker.targetInfo(pos + 1, targetType); + pos = subWalker.typePath(pos); + pos = annotation(pos); + } + + return pos; + } + } + + static class SubWalker { + byte[] info; + + SubWalker(byte[] attrInfo) { + info = attrInfo; + } + + final int targetInfo(int pos, int type) throws Exception { + switch (type) { + case 0x00: + case 0x01: { + int index = info[pos] & 0xff; + typeParameterTarget(pos, type, index); + return pos + 1; } + case 0x10: { + int index = ByteArray.readU16bit(info, pos); + supertypeTarget(pos, index); + return pos + 2; } + case 0x11: + case 0x12: { + int param = info[pos] & 0xff; + int bound = info[pos + 1] & 0xff; + typeParameterBoundTarget(pos, type, param, bound); + return pos + 2; } + case 0x13: + case 0x14: + case 0x15: + emptyTarget(pos, type); + return pos; + case 0x16: { + int index = info[pos] & 0xff; + formalParameterTarget(pos, index); + return pos + 1; } + case 0x17: { + int index = ByteArray.readU16bit(info, pos); + throwsTarget(pos, index); + return pos + 2; } + case 0x40: + case 0x41: { + int len = ByteArray.readU16bit(info, pos); + return localvarTarget(pos + 2, type, len); } + case 0x42: { + int index = ByteArray.readU16bit(info, pos); + catchTarget(pos, index); + return pos + 2; } + case 0x43: + case 0x44: + case 0x45: + case 0x46: { + int offset = ByteArray.readU16bit(info, pos); + offsetTarget(pos, type, offset); + return pos + 2; } + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: { + int offset = ByteArray.readU16bit(info, pos); + int index = info[pos + 2] & 0xff; + typeArgumentTarget(pos, type, offset, index); + return pos + 3; } + default: + throw new RuntimeException("invalid target type: " + type); + } + } + + void typeParameterTarget(int pos, int targetType, int typeParameterIndex) + throws Exception {} + + void supertypeTarget(int pos, int superTypeIndex) throws Exception {} + + void typeParameterBoundTarget(int pos, int targetType, int typeParameterIndex, + int boundIndex) throws Exception {} + + void emptyTarget(int pos, int targetType) throws Exception {} + + void formalParameterTarget(int pos, int formalParameterIndex) throws Exception {} + + void throwsTarget(int pos, int throwsTypeIndex) throws Exception {} + + int localvarTarget(int pos, int targetType, int tableLength) throws Exception { + for (int i = 0; i < tableLength; i++) { + int start = ByteArray.readU16bit(info, pos); + int length = ByteArray.readU16bit(info, pos + 2); + int index = ByteArray.readU16bit(info, pos + 4); + localvarTarget(pos, targetType, start, length, index); + pos += 6; + } + + return pos; + } + + void localvarTarget(int pos, int targetType, int startPc, int length, int index) + throws Exception {} + + void catchTarget(int pos, int exceptionTableIndex) throws Exception {} + + void offsetTarget(int pos, int targetType, int offset) throws Exception {} + + void typeArgumentTarget(int pos, int targetType, int offset, int typeArgumentIndex) + throws Exception {} + + final int typePath(int pos) throws Exception { + int len = info[pos++] & 0xff; + return typePath(pos, len); + } + + int typePath(int pos, int pathLength) throws Exception { + for (int i = 0; i < pathLength; i++) { + int kind = info[pos] & 0xff; + int index = info[pos + 1] & 0xff; + typePath(pos, kind, index); + pos += 2; + } + + return pos; + } + + void typePath(int pos, int typePathKind, int typeArgumentIndex) throws Exception {} + } + + static class Renamer extends AnnotationsAttribute.Renamer { + SubWalker sub; + + Renamer(byte[] attrInfo, ConstPool cp, Map map) { + super(attrInfo, cp, map); + sub = new SubWalker(attrInfo); + } + + int annotationArray(int pos, int num) throws Exception { + for (int i = 0; i < num; i++) { + int targetType = info[pos] & 0xff; + pos = sub.targetInfo(pos + 1, targetType); + pos = sub.typePath(pos); + pos = annotation(pos); + } + + return pos; + } + } + + static class Copier extends AnnotationsAttribute.Copier { + SubCopier sub; + + Copier(byte[] attrInfo, ConstPool src, ConstPool dest, Map map) { + super(attrInfo, src, dest, map, false); + TypeAnnotationsWriter w = new TypeAnnotationsWriter(output, dest); + writer = w; + sub = new SubCopier(attrInfo, src, dest, map, w); + } + + int annotationArray(int pos, int num) throws Exception { + writer.numAnnotations(num); + for (int i = 0; i < num; i++) { + int targetType = info[pos] & 0xff; + pos = sub.targetInfo(pos + 1, targetType); + pos = sub.typePath(pos); + pos = annotation(pos); + } + + return pos; + } + } + + static class SubCopier extends SubWalker { + ConstPool srcPool, destPool; + Map classnames; + TypeAnnotationsWriter writer; + + SubCopier(byte[] attrInfo, ConstPool src, ConstPool dest, Map map, + TypeAnnotationsWriter w) + { + super(attrInfo); + srcPool = src; + destPool = dest; + classnames = map; + writer = w; + } + + void typeParameterTarget(int pos, int targetType, int typeParameterIndex) + throws Exception + { + writer.typeParameterTarget(targetType, typeParameterIndex); + } + + void supertypeTarget(int pos, int superTypeIndex) throws Exception { + writer.supertypeTarget(superTypeIndex); + } + + void typeParameterBoundTarget(int pos, int targetType, int typeParameterIndex, + int boundIndex) + throws Exception + { + writer.typeParameterBoundTarget(targetType, typeParameterIndex, boundIndex); + } + + void emptyTarget(int pos, int targetType) throws Exception { + writer.emptyTarget(targetType); + } + + void formalParameterTarget(int pos, int formalParameterIndex) throws Exception { + writer.formalParameterTarget(formalParameterIndex); + } + + void throwsTarget(int pos, int throwsTypeIndex) throws Exception { + writer.throwsTarget(throwsTypeIndex); + } + + int localvarTarget(int pos, int targetType, int tableLength) throws Exception { + writer.localVarTarget(targetType, tableLength); + return super.localvarTarget(pos, targetType, tableLength); + } + + void localvarTarget(int pos, int targetType, int startPc, int length, int index) + throws Exception + { + writer.localVarTargetTable(startPc, length, index); + } + + void catchTarget(int pos, int exceptionTableIndex) throws Exception { + writer.catchTarget(exceptionTableIndex); + } + + void offsetTarget(int pos, int targetType, int offset) throws Exception { + writer.offsetTarget(targetType, offset); + } + + void typeArgumentTarget(int pos, int targetType, int offset, int typeArgumentIndex) + throws Exception + { + writer.typeArgumentTarget(targetType, offset, typeArgumentIndex); + } + + int typePath(int pos, int pathLength) throws Exception { + writer.typePath(pathLength); + return super.typePath(pos, pathLength); + } + + void typePath(int pos, int typePathKind, int typeArgumentIndex) throws Exception { + writer.typePathPath(typePathKind, typeArgumentIndex); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java new file mode 100644 index 0000000..e70d005 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java @@ -0,0 +1,423 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import java.util.Iterator; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.AccessFlag; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.CodeAttribute; +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.bytecode.ExceptionTable; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; +import com.wenshuo.agent.javassist.bytecode.Opcode; + +/** + * A data-flow analyzer that determines the type state of the stack and local + * variable table at every reachable instruction in a method. During analysis, + * bytecode verification is performed in a similar manner to that described + * in the JVM specification. + * + *

Example:

+ * + *
+ * // Method to analyze
+ * public Object doSomething(int x) {
+ *     Number n;
+ *     if (x < 5) {
+ *        n = new Double(0);
+ *     } else {
+ *        n = new Long(0);
+ *     }
+ *
+ *     return n;
+ * }
+ *
+ * // Which compiles to:
+ * // 0:   iload_1
+ * // 1:   iconst_5
+ * // 2:   if_icmpge   17
+ * // 5:   new #18; //class java/lang/Double
+ * // 8:   dup
+ * // 9:   dconst_0
+ * // 10:  invokespecial   #44; //Method java/lang/Double."<init>":(D)V
+ * // 13:  astore_2
+ * // 14:  goto    26
+ * // 17:  new #16; //class java/lang/Long
+ * // 20:  dup
+ * // 21:  lconst_1
+ * // 22:  invokespecial   #47; //Method java/lang/Long."<init>":(J)V
+ * // 25:  astore_2
+ * // 26:  aload_2
+ * // 27:  areturn
+ *
+ * public void analyzeIt(CtClass clazz, MethodInfo method) {
+ *     Analyzer analyzer = new Analyzer();
+ *     Frame[] frames = analyzer.analyze(clazz, method);
+ *     frames[0].getLocal(0).getCtClass(); // returns clazz;
+ *     frames[0].getLocal(1).getCtClass(); // returns java.lang.String
+ *     frames[1].peek(); // returns Type.INTEGER
+ *     frames[27].peek().getCtClass(); // returns java.lang.Number
+ * }
+ * 
+ * + * @see FramePrinter + * @author Jason T. Greene + */ +public class Analyzer implements Opcode { + private final SubroutineScanner scanner = new SubroutineScanner(); + private CtClass clazz; + private ExceptionInfo[] exceptions; + private Frame[] frames; + private Subroutine[] subroutines; + + private static class ExceptionInfo { + private int end; + private int handler; + private int start; + private Type type; + + private ExceptionInfo(int start, int end, int handler, Type type) { + this.start = start; + this.end = end; + this.handler = handler; + this.type = type; + } + } + + /** + * Performs data-flow analysis on a method and returns an array, indexed by + * instruction position, containing the starting frame state of all reachable + * instructions. Non-reachable code, and illegal code offsets are represented + * as a null in the frame state array. This can be used to detect dead code. + * + * If the method does not contain code (it is either native or abstract), null + * is returned. + * + * @param clazz the declaring class of the method + * @param method the method to analyze + * @return an array, indexed by instruction position, of the starting frame state, + * or null if this method doesn't have code + * @throws BadBytecode if the bytecode does not comply with the JVM specification + */ + public Frame[] analyze(CtClass clazz, MethodInfo method) throws BadBytecode { + this.clazz = clazz; + CodeAttribute codeAttribute = method.getCodeAttribute(); + // Native or Abstract + if (codeAttribute == null) + return null; + + int maxLocals = codeAttribute.getMaxLocals(); + int maxStack = codeAttribute.getMaxStack(); + int codeLength = codeAttribute.getCodeLength(); + + CodeIterator iter = codeAttribute.iterator(); + IntQueue queue = new IntQueue(); + + exceptions = buildExceptionInfo(method); + subroutines = scanner.scan(method); + + Executor executor = new Executor(clazz.getClassPool(), method.getConstPool()); + frames = new Frame[codeLength]; + frames[iter.lookAhead()] = firstFrame(method, maxLocals, maxStack); + queue.add(iter.next()); + while (!queue.isEmpty()) { + analyzeNextEntry(method, iter, queue, executor); + } + + return frames; + } + + /** + * Performs data-flow analysis on a method and returns an array, indexed by + * instruction position, containing the starting frame state of all reachable + * instructions. Non-reachable code, and illegal code offsets are represented + * as a null in the frame state array. This can be used to detect dead code. + * + * If the method does not contain code (it is either native or abstract), null + * is returned. + * + * @param method the method to analyze + * @return an array, indexed by instruction position, of the starting frame state, + * or null if this method doesn't have code + * @throws BadBytecode if the bytecode does not comply with the JVM specification + */ + public Frame[] analyze(CtMethod method) throws BadBytecode { + return analyze(method.getDeclaringClass(), method.getMethodInfo2()); + } + + private void analyzeNextEntry(MethodInfo method, CodeIterator iter, + IntQueue queue, Executor executor) throws BadBytecode { + int pos = queue.take(); + iter.move(pos); + iter.next(); + + Frame frame = frames[pos].copy(); + Subroutine subroutine = subroutines[pos]; + + try { + executor.execute(method, pos, iter, frame, subroutine); + } catch (RuntimeException e) { + throw new BadBytecode(e.getMessage() + "[pos = " + pos + "]", e); + } + + int opcode = iter.byteAt(pos); + + if (opcode == TABLESWITCH) { + mergeTableSwitch(queue, pos, iter, frame); + } else if (opcode == LOOKUPSWITCH) { + mergeLookupSwitch(queue, pos, iter, frame); + } else if (opcode == RET) { + mergeRet(queue, iter, pos, frame, subroutine); + } else if (Util.isJumpInstruction(opcode)) { + int target = Util.getJumpTarget(pos, iter); + + if (Util.isJsr(opcode)) { + // Merge the state before the jsr into the next instruction + mergeJsr(queue, frames[pos], subroutines[target], pos, lookAhead(iter, pos)); + } else if (! Util.isGoto(opcode)) { + merge(queue, frame, lookAhead(iter, pos)); + } + + merge(queue, frame, target); + } else if (opcode != ATHROW && ! Util.isReturn(opcode)) { + // Can advance to next instruction + merge(queue, frame, lookAhead(iter, pos)); + } + + // Merge all exceptions that are reachable from this instruction. + // The redundancy is intentional, since the state must be based + // on the current instruction frame. + mergeExceptionHandlers(queue, method, pos, frame); + } + + private ExceptionInfo[] buildExceptionInfo(MethodInfo method) { + ConstPool constPool = method.getConstPool(); + ClassPool classes = clazz.getClassPool(); + + ExceptionTable table = method.getCodeAttribute().getExceptionTable(); + ExceptionInfo[] exceptions = new ExceptionInfo[table.size()]; + for (int i = 0; i < table.size(); i++) { + int index = table.catchType(i); + Type type; + try { + type = index == 0 ? Type.THROWABLE : Type.get(classes.get(constPool.getClassInfo(index))); + } catch (NotFoundException e) { + throw new IllegalStateException(e.getMessage()); + } + + exceptions[i] = new ExceptionInfo(table.startPc(i), table.endPc(i), table.handlerPc(i), type); + } + + return exceptions; + } + + private Frame firstFrame(MethodInfo method, int maxLocals, int maxStack) { + int pos = 0; + + Frame first = new Frame(maxLocals, maxStack); + if ((method.getAccessFlags() & AccessFlag.STATIC) == 0) { + first.setLocal(pos++, Type.get(clazz)); + } + + CtClass[] parameters; + try { + parameters = Descriptor.getParameterTypes(method.getDescriptor(), clazz.getClassPool()); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + + for (int i = 0; i < parameters.length; i++) { + Type type = zeroExtend(Type.get(parameters[i])); + first.setLocal(pos++, type); + if (type.getSize() == 2) + first.setLocal(pos++, Type.TOP); + } + + return first; + } + + private int getNext(CodeIterator iter, int of, int restore) throws BadBytecode { + iter.move(of); + iter.next(); + int next = iter.lookAhead(); + iter.move(restore); + iter.next(); + + return next; + } + + private int lookAhead(CodeIterator iter, int pos) throws BadBytecode { + if (! iter.hasNext()) + throw new BadBytecode("Execution falls off end! [pos = " + pos + "]"); + + return iter.lookAhead(); + } + + + private void merge(IntQueue queue, Frame frame, int target) { + Frame old = frames[target]; + boolean changed; + + if (old == null) { + frames[target] = frame.copy(); + changed = true; + } else { + changed = old.merge(frame); + } + + if (changed) { + queue.add(target); + } + } + + private void mergeExceptionHandlers(IntQueue queue, MethodInfo method, int pos, Frame frame) { + for (int i = 0; i < exceptions.length; i++) { + ExceptionInfo exception = exceptions[i]; + + // Start is inclusive, while end is exclusive! + if (pos >= exception.start && pos < exception.end) { + Frame newFrame = frame.copy(); + newFrame.clearStack(); + newFrame.push(exception.type); + merge(queue, newFrame, exception.handler); + } + } + } + + private void mergeJsr(IntQueue queue, Frame frame, Subroutine sub, int pos, int next) throws BadBytecode { + if (sub == null) + throw new BadBytecode("No subroutine at jsr target! [pos = " + pos + "]"); + + Frame old = frames[next]; + boolean changed = false; + + if (old == null) { + old = frames[next] = frame.copy(); + changed = true; + } else { + for (int i = 0; i < frame.localsLength(); i++) { + // Skip everything accessed by a subroutine, mergeRet must handle this + if (!sub.isAccessed(i)) { + Type oldType = old.getLocal(i); + Type newType = frame.getLocal(i); + if (oldType == null) { + old.setLocal(i, newType); + changed = true; + continue; + } + + newType = oldType.merge(newType); + // Always set the type, in case a multi-type switched to a standard type. + old.setLocal(i, newType); + if (!newType.equals(oldType) || newType.popChanged()) + changed = true; + } + } + } + + if (! old.isJsrMerged()) { + old.setJsrMerged(true); + changed = true; + } + + if (changed && old.isRetMerged()) + queue.add(next); + + } + + private void mergeLookupSwitch(IntQueue queue, int pos, CodeIterator iter, Frame frame) throws BadBytecode { + int index = (pos & ~3) + 4; + // default + merge(queue, frame, pos + iter.s32bitAt(index)); + int npairs = iter.s32bitAt(index += 4); + int end = npairs * 8 + (index += 4); + + // skip "match" + for (index += 4; index < end; index += 8) { + int target = iter.s32bitAt(index) + pos; + merge(queue, frame, target); + } + } + + private void mergeRet(IntQueue queue, CodeIterator iter, int pos, Frame frame, Subroutine subroutine) throws BadBytecode { + if (subroutine == null) + throw new BadBytecode("Ret on no subroutine! [pos = " + pos + "]"); + + Iterator callerIter = subroutine.callers().iterator(); + while (callerIter.hasNext()) { + int caller = ((Integer) callerIter.next()).intValue(); + int returnLoc = getNext(iter, caller, pos); + boolean changed = false; + + Frame old = frames[returnLoc]; + if (old == null) { + old = frames[returnLoc] = frame.copyStack(); + changed = true; + } else { + changed = old.mergeStack(frame); + } + + for (Iterator i = subroutine.accessed().iterator(); i.hasNext(); ) { + int index = ((Integer)i.next()).intValue(); + Type oldType = old.getLocal(index); + Type newType = frame.getLocal(index); + if (oldType != newType) { + old.setLocal(index, newType); + changed = true; + } + } + + if (! old.isRetMerged()) { + old.setRetMerged(true); + changed = true; + } + + if (changed && old.isJsrMerged()) + queue.add(returnLoc); + } + } + + + private void mergeTableSwitch(IntQueue queue, int pos, CodeIterator iter, Frame frame) throws BadBytecode { + // Skip 4 byte alignment padding + int index = (pos & ~3) + 4; + // default + merge(queue, frame, pos + iter.s32bitAt(index)); + int low = iter.s32bitAt(index += 4); + int high = iter.s32bitAt(index += 4); + int end = (high - low + 1) * 4 + (index += 4); + + // Offset table + for (; index < end; index += 4) { + int target = iter.s32bitAt(index) + pos; + merge(queue, frame, target); + } + } + + private Type zeroExtend(Type type) { + if (type == Type.SHORT || type == Type.BYTE || type == Type.CHAR || type == Type.BOOLEAN) + return Type.INTEGER; + + return type; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java new file mode 100644 index 0000000..d37d84c --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java @@ -0,0 +1,504 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.analysis; + +import java.util.ArrayList; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; +import com.wenshuo.agent.javassist.bytecode.stackmap.BasicBlock; + +/** + * Represents the control flow graph of a given method. + * + *

To obtain the control flow graph, do the following:

+ * + *
CtMethod m = ...
+ * ControlFlow cf = new ControlFlow(m);
+ * Block[] blocks = cf.basicBlocks();
+ * 
+ * + *

blocks is an array of basic blocks in + * that method body.

+ * + * @see javassist.CtMethod + * @see Block + * @see Frame + * @see Analyzer + * @author Shigeru Chiba + * @since 3.16 + */ +public class ControlFlow { + private CtClass clazz; + private MethodInfo methodInfo; + private Block[] basicBlocks; + private Frame[] frames; + + /** + * Constructs a control-flow analyzer for the given method. + */ + public ControlFlow(CtMethod method) throws BadBytecode { + this(method.getDeclaringClass(), method.getMethodInfo2()); + } + + /** + * Constructs a control-flow analyzer. + */ + public ControlFlow(CtClass ctclazz, MethodInfo minfo) throws BadBytecode { + clazz = ctclazz; + methodInfo = minfo; + frames = null; + basicBlocks = (Block[])new BasicBlock.Maker() { + protected BasicBlock makeBlock(int pos) { + return new Block(pos, methodInfo); + } + protected BasicBlock[] makeArray(int size) { + return new Block[size]; + } + }.make(minfo); + int size = basicBlocks.length; + int[] counters = new int[size]; + for (int i = 0; i < size; i++) { + Block b = basicBlocks[i]; + b.index = i; + b.entrances = new Block[b.incomings()]; + counters[i] = 0; + } + + for (int i = 0; i < size; i++) { + Block b = basicBlocks[i]; + for (int k = 0; k < b.exits(); k++) { + Block e = b.exit(k); + e.entrances[counters[e.index]++] = b; + } + + ControlFlow.Catcher[] catchers = b.catchers(); + for (int k = 0; k < catchers.length; k++) { + Block catchBlock = catchers[k].node; + catchBlock.entrances[counters[catchBlock.index]++] = b; + } + } + } + + /** + * Returns all the basic blocks in the method body. + */ + public Block[] basicBlocks() { + return basicBlocks; + } + + /** + * Returns the types of the local variables and stack frame entries + * available at the given position. If the byte at the position is + * not the first byte of an instruction, then this method returns + * null. + * + * @param pos the position. + */ + public Frame frameAt(int pos) throws BadBytecode { + if (frames == null) + frames = new Analyzer().analyze(clazz, methodInfo); + + return frames[pos]; + } + + /** + * Constructs a dominator tree. This method returns an array of + * the tree nodes. The first element of the array is the root + * of the tree. + * + *

The order of the elements is the same as that + * of the elements in the Block array returned + * by the basicBlocks + * method. If a Block object is at the i-th position + * in the Block array, then + * the Node object referring to that + * Block object is at the i-th position in the + * array returned by this method. + * For every array element node, its index in the + * array is equivalent to node.block().index(). + * + * @return an array of the tree nodes, or null if the method is abstract. + * @see Node#block() + * @see Block#index() + */ + public Node[] dominatorTree() { + int size = basicBlocks.length; + if (size == 0) + return null; + + Node[] nodes = new Node[size]; + boolean[] visited = new boolean[size]; + int[] distance = new int[size]; + for (int i = 0; i < size; i++) { + nodes[i] = new Node(basicBlocks[i]); + visited[i] = false; + } + + Access access = new Access(nodes) { + BasicBlock[] exits(Node n) { return n.block.getExit(); } + BasicBlock[] entrances(Node n) { return n.block.entrances; } + }; + nodes[0].makeDepth1stTree(null, visited, 0, distance, access); + do { + for (int i = 0; i < size; i++) + visited[i] = false; + } while (nodes[0].makeDominatorTree(visited, distance, access)); + Node.setChildren(nodes); + return nodes; + } + + /** + * Constructs a post dominator tree. This method returns an array of + * the tree nodes. Note that the tree has multiple roots. + * The parent of the root nodes is null. + * + *

The order of the elements is the same as that + * of the elements in the Block array returned + * by the basicBlocks + * method. If a Block object is at the i-th position + * in the Block array, then + * the Node object referring to that + * Block object is at the i-th position in the + * array returned by this method. + * For every array element node, its index in the + * array is equivalent to node.block().index(). + * + * @return an array of the tree nodes, or null if the method is abstract. + * @see Node#block() + * @see Block#index() + */ + public Node[] postDominatorTree() { + int size = basicBlocks.length; + if (size == 0) + return null; + + Node[] nodes = new Node[size]; + boolean[] visited = new boolean[size]; + int[] distance = new int[size]; + for (int i = 0; i < size; i++) { + nodes[i] = new Node(basicBlocks[i]); + visited[i] = false; + } + + Access access = new Access(nodes) { + BasicBlock[] exits(Node n) { return n.block.entrances; } + BasicBlock[] entrances(Node n) { return n.block.getExit(); } + }; + + int counter = 0; + for (int i = 0; i < size; i++) + if (nodes[i].block.exits() == 0) + counter = nodes[i].makeDepth1stTree(null, visited, counter, distance, access); + + boolean changed; + do { + for (int i = 0; i < size; i++) + visited[i] = false; + + changed = false; + for (int i = 0; i < size; i++) + if (nodes[i].block.exits() == 0) + if (nodes[i].makeDominatorTree(visited, distance, access)) + changed = true; + } while (changed); + + Node.setChildren(nodes); + return nodes; + } + + /** + * Basic block. + * It is a sequence of contiguous instructions that do not contain + * jump/branch instructions except the last one. + * Since Java6 or later does not allow JSR, + * we deal with JSR as a non-branch instruction. + */ + public static class Block extends BasicBlock { + /** + * A field that can be freely used for storing extra data. + * A client program of this control-flow analyzer can append + * an additional attribute to a Block object. + * The Javassist library never accesses this field. + */ + public Object clientData = null; + + int index; + MethodInfo method; + Block[] entrances; + + Block(int pos, MethodInfo minfo) { + super(pos); + method = minfo; + } + + protected void toString2(StringBuffer sbuf) { + super.toString2(sbuf); + sbuf.append(", incoming{"); + for (int i = 0; i < entrances.length; i++) + sbuf.append(entrances[i].position).append(", "); + + sbuf.append("}"); + } + + BasicBlock[] getExit() { return exit; } + + /** + * Returns the position of this block in the array of + * basic blocks that the basicBlocks method + * returns. + * + * @see #basicBlocks() + */ + public int index() { return index; } + + /** + * Returns the position of the first instruction + * in this block. + */ + public int position() { return position; } + + /** + * Returns the length of this block. + */ + public int length() { return length; } + + /** + * Returns the number of the control paths entering this block. + */ + public int incomings() { return incoming; } + + /** + * Returns the block that the control may jump into this block from. + */ + public Block incoming(int n) { + return entrances[n]; + } + + /** + * Return the number of the blocks that may be executed + * after this block. + */ + public int exits() { return exit == null ? 0 : exit.length; } + + /** + * Returns the n-th block that may be executed after this + * block. + * + * @param n an index in the array of exit blocks. + */ + public Block exit(int n) { return (Block)exit[n]; } + + /** + * Returns catch clauses that will catch an exception thrown + * in this block. + */ + public Catcher[] catchers() { + ArrayList catchers = new ArrayList(); + BasicBlock.Catch c = toCatch; + while (c != null) { + catchers.add(new Catcher(c)); + c = c.next; + } + + return (Catcher[])catchers.toArray(new Catcher[catchers.size()]); + } + } + + static abstract class Access { + Node[] all; + Access(Node[] nodes) { all = nodes; } + Node node(BasicBlock b) { return all[((Block)b).index]; } + abstract BasicBlock[] exits(Node n); + abstract BasicBlock[] entrances(Node n); + } + + /** + * A node of (post) dominator trees. + */ + public static class Node { + private Block block; + private Node parent; + private Node[] children; + + Node(Block b) { + block = b; + parent = null; + } + + /** + * Returns a String representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(); + sbuf.append("Node[pos=").append(block().position()); + sbuf.append(", parent="); + sbuf.append(parent == null ? "*" : Integer.toString(parent.block().position())); + sbuf.append(", children{"); + for (int i = 0; i < children.length; i++) + sbuf.append(children[i].block().position()).append(", "); + + sbuf.append("}]"); + return sbuf.toString(); + } + + /** + * Returns the basic block indicated by this node. + */ + public Block block() { return block; } + + /** + * Returns the parent of this node. + */ + public Node parent() { return parent; } + + /** + * Returns the number of the children of this node. + */ + public int children() { return children.length; } + + /** + * Returns the n-th child of this node. + * + * @param n an index in the array of children. + */ + public Node child(int n) { return children[n]; } + + /* + * After executing this method, distance[] represents the post order of the tree nodes. + * It also represents distances from the root; a bigger number represents a shorter + * distance. parent is set to its parent in the depth first spanning tree. + */ + int makeDepth1stTree(Node caller, boolean[] visited, int counter, int[] distance, Access access) { + int index = block.index; + if (visited[index]) + return counter; + + visited[index] = true; + parent = caller; + BasicBlock[] exits = access.exits(this); + if (exits != null) + for (int i = 0; i < exits.length; i++) { + Node n = access.node(exits[i]); + counter = n.makeDepth1stTree(this, visited, counter, distance, access); + } + + distance[index] = counter++; + return counter; + } + + boolean makeDominatorTree(boolean[] visited, int[] distance, Access access) { + int index = block.index; + if (visited[index]) + return false; + + visited[index] = true; + boolean changed = false; + BasicBlock[] exits = access.exits(this); + if (exits != null) + for (int i = 0; i < exits.length; i++) { + Node n = access.node(exits[i]); + if (n.makeDominatorTree(visited, distance, access)) + changed = true; + } + + BasicBlock[] entrances = access.entrances(this); + if (entrances != null) + for (int i = 0; i < entrances.length; i++) { + if (parent != null) { + Node n = getAncestor(parent, access.node(entrances[i]), distance); + if (n != parent) { + parent = n; + changed = true; + } + } + } + + return changed; + } + + private static Node getAncestor(Node n1, Node n2, int[] distance) { + while (n1 != n2) { + if (distance[n1.block.index] < distance[n2.block.index]) + n1 = n1.parent; + else + n2 = n2.parent; + + if (n1 == null || n2 == null) + return null; + } + + return n1; + } + + private static void setChildren(Node[] all) { + int size = all.length; + int[] nchildren = new int[size]; + for (int i = 0; i < size; i++) + nchildren[i] = 0; + + for (int i = 0; i < size; i++) { + Node p = all[i].parent; + if (p != null) + nchildren[p.block.index]++; + } + + for (int i = 0; i < size; i++) + all[i].children = new Node[nchildren[i]]; + + for (int i = 0; i < size; i++) + nchildren[i] = 0; + + for (int i = 0; i < size; i++) { + Node n = all[i]; + Node p = n.parent; + if (p != null) + p.children[nchildren[p.block.index]++] = n; + } + } + } + + /** + * Represents a catch clause. + */ + public static class Catcher { + private Block node; + private int typeIndex; + + Catcher(BasicBlock.Catch c) { + node = (Block)c.body; + typeIndex = c.typeIndex; + } + + /** + * Returns the first block of the catch clause. + */ + public Block block() { return node; } + + /** + * Returns the name of the exception type that + * this catch clause catches. + */ + public String type() { + if (typeIndex == 0) + return "java.lang.Throwable"; + else + return node.method.getConstPool().getClassInfo(typeIndex); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java new file mode 100644 index 0000000..76e7e82 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java @@ -0,0 +1,1047 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; +import com.wenshuo.agent.javassist.bytecode.Opcode; + +/** + * Executor is responsible for modeling the effects of a JVM instruction on a frame. + * + * @author Jason T. Greene + */ +public class Executor implements Opcode { + private final ConstPool constPool; + private final ClassPool classPool; + private final Type STRING_TYPE; + private final Type CLASS_TYPE; + private final Type THROWABLE_TYPE; + private int lastPos; + + public Executor(ClassPool classPool, ConstPool constPool) { + this.constPool = constPool; + this.classPool = classPool; + + try { + STRING_TYPE = getType("java.lang.String"); + CLASS_TYPE = getType("java.lang.Class"); + THROWABLE_TYPE = getType("java.lang.Throwable"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /** + * Execute the instruction, modeling the effects on the specified frame and subroutine. + * If a subroutine is passed, the access flags will be modified if this instruction accesses + * the local variable table. + * + * @param method the method containing the instruction + * @param pos the position of the instruction in the method + * @param iter the code iterator used to find the instruction + * @param frame the frame to modify to represent the result of the instruction + * @param subroutine the optional subroutine this instruction belongs to. + * @throws BadBytecode if the bytecode violates the jvm spec + */ + public void execute(MethodInfo method, int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode { + this.lastPos = pos; + int opcode = iter.byteAt(pos); + + + // Declared opcode in order + switch (opcode) { + case NOP: + break; + case ACONST_NULL: + frame.push(Type.UNINIT); + break; + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + frame.push(Type.INTEGER); + break; + case LCONST_0: + case LCONST_1: + frame.push(Type.LONG); + frame.push(Type.TOP); + break; + case FCONST_0: + case FCONST_1: + case FCONST_2: + frame.push(Type.FLOAT); + break; + case DCONST_0: + case DCONST_1: + frame.push(Type.DOUBLE); + frame.push(Type.TOP); + break; + case BIPUSH: + case SIPUSH: + frame.push(Type.INTEGER); + break; + case LDC: + evalLDC(iter.byteAt(pos + 1), frame); + break; + case LDC_W : + case LDC2_W : + evalLDC(iter.u16bitAt(pos + 1), frame); + break; + case ILOAD: + evalLoad(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine); + break; + case LLOAD: + evalLoad(Type.LONG, iter.byteAt(pos + 1), frame, subroutine); + break; + case FLOAD: + evalLoad(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine); + break; + case DLOAD: + evalLoad(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine); + break; + case ALOAD: + evalLoad(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine); + break; + case ILOAD_0: + case ILOAD_1: + case ILOAD_2: + case ILOAD_3: + evalLoad(Type.INTEGER, opcode - ILOAD_0, frame, subroutine); + break; + case LLOAD_0: + case LLOAD_1: + case LLOAD_2: + case LLOAD_3: + evalLoad(Type.LONG, opcode - LLOAD_0, frame, subroutine); + break; + case FLOAD_0: + case FLOAD_1: + case FLOAD_2: + case FLOAD_3: + evalLoad(Type.FLOAT, opcode - FLOAD_0, frame, subroutine); + break; + case DLOAD_0: + case DLOAD_1: + case DLOAD_2: + case DLOAD_3: + evalLoad(Type.DOUBLE, opcode - DLOAD_0, frame, subroutine); + break; + case ALOAD_0: + case ALOAD_1: + case ALOAD_2: + case ALOAD_3: + evalLoad(Type.OBJECT, opcode - ALOAD_0, frame, subroutine); + break; + case IALOAD: + evalArrayLoad(Type.INTEGER, frame); + break; + case LALOAD: + evalArrayLoad(Type.LONG, frame); + break; + case FALOAD: + evalArrayLoad(Type.FLOAT, frame); + break; + case DALOAD: + evalArrayLoad(Type.DOUBLE, frame); + break; + case AALOAD: + evalArrayLoad(Type.OBJECT, frame); + break; + case BALOAD: + case CALOAD: + case SALOAD: + evalArrayLoad(Type.INTEGER, frame); + break; + case ISTORE: + evalStore(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine); + break; + case LSTORE: + evalStore(Type.LONG, iter.byteAt(pos + 1), frame, subroutine); + break; + case FSTORE: + evalStore(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine); + break; + case DSTORE: + evalStore(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine); + break; + case ASTORE: + evalStore(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine); + break; + case ISTORE_0: + case ISTORE_1: + case ISTORE_2: + case ISTORE_3: + evalStore(Type.INTEGER, opcode - ISTORE_0, frame, subroutine); + break; + case LSTORE_0: + case LSTORE_1: + case LSTORE_2: + case LSTORE_3: + evalStore(Type.LONG, opcode - LSTORE_0, frame, subroutine); + break; + case FSTORE_0: + case FSTORE_1: + case FSTORE_2: + case FSTORE_3: + evalStore(Type.FLOAT, opcode - FSTORE_0, frame, subroutine); + break; + case DSTORE_0: + case DSTORE_1: + case DSTORE_2: + case DSTORE_3: + evalStore(Type.DOUBLE, opcode - DSTORE_0, frame, subroutine); + break; + case ASTORE_0: + case ASTORE_1: + case ASTORE_2: + case ASTORE_3: + evalStore(Type.OBJECT, opcode - ASTORE_0, frame, subroutine); + break; + case IASTORE: + evalArrayStore(Type.INTEGER, frame); + break; + case LASTORE: + evalArrayStore(Type.LONG, frame); + break; + case FASTORE: + evalArrayStore(Type.FLOAT, frame); + break; + case DASTORE: + evalArrayStore(Type.DOUBLE, frame); + break; + case AASTORE: + evalArrayStore(Type.OBJECT, frame); + break; + case BASTORE: + case CASTORE: + case SASTORE: + evalArrayStore(Type.INTEGER, frame); + break; + case POP: + if (frame.pop() == Type.TOP) + throw new BadBytecode("POP can not be used with a category 2 value, pos = " + pos); + break; + case POP2: + frame.pop(); + frame.pop(); + break; + case DUP: { + Type type = frame.peek(); + if (type == Type.TOP) + throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos); + + frame.push(frame.peek()); + break; + } + case DUP_X1: + case DUP_X2: { + Type type = frame.peek(); + if (type == Type.TOP) + throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos); + int end = frame.getTopIndex(); + int insert = end - (opcode - DUP_X1) - 1; + frame.push(type); + + while (end > insert) { + frame.setStack(end, frame.getStack(end - 1)); + end--; + } + frame.setStack(insert, type); + break; + } + case DUP2: + frame.push(frame.getStack(frame.getTopIndex() - 1)); + frame.push(frame.getStack(frame.getTopIndex() - 1)); + break; + case DUP2_X1: + case DUP2_X2: { + int end = frame.getTopIndex(); + int insert = end - (opcode - DUP2_X1) - 1; + Type type1 = frame.getStack(frame.getTopIndex() - 1); + Type type2 = frame.peek(); + frame.push(type1); + frame.push(type2); + while (end > insert) { + frame.setStack(end, frame.getStack(end - 2)); + end--; + } + frame.setStack(insert, type2); + frame.setStack(insert - 1, type1); + break; + } + case SWAP: { + Type type1 = frame.pop(); + Type type2 = frame.pop(); + if (type1.getSize() == 2 || type2.getSize() == 2) + throw new BadBytecode("Swap can not be used with category 2 values, pos = " + pos); + frame.push(type1); + frame.push(type2); + break; + } + + // Math + case IADD: + evalBinaryMath(Type.INTEGER, frame); + break; + case LADD: + evalBinaryMath(Type.LONG, frame); + break; + case FADD: + evalBinaryMath(Type.FLOAT, frame); + break; + case DADD: + evalBinaryMath(Type.DOUBLE, frame); + break; + case ISUB: + evalBinaryMath(Type.INTEGER, frame); + break; + case LSUB: + evalBinaryMath(Type.LONG, frame); + break; + case FSUB: + evalBinaryMath(Type.FLOAT, frame); + break; + case DSUB: + evalBinaryMath(Type.DOUBLE, frame); + break; + case IMUL: + evalBinaryMath(Type.INTEGER, frame); + break; + case LMUL: + evalBinaryMath(Type.LONG, frame); + break; + case FMUL: + evalBinaryMath(Type.FLOAT, frame); + break; + case DMUL: + evalBinaryMath(Type.DOUBLE, frame); + break; + case IDIV: + evalBinaryMath(Type.INTEGER, frame); + break; + case LDIV: + evalBinaryMath(Type.LONG, frame); + break; + case FDIV: + evalBinaryMath(Type.FLOAT, frame); + break; + case DDIV: + evalBinaryMath(Type.DOUBLE, frame); + break; + case IREM: + evalBinaryMath(Type.INTEGER, frame); + break; + case LREM: + evalBinaryMath(Type.LONG, frame); + break; + case FREM: + evalBinaryMath(Type.FLOAT, frame); + break; + case DREM: + evalBinaryMath(Type.DOUBLE, frame); + break; + + // Unary + case INEG: + verifyAssignable(Type.INTEGER, simplePeek(frame)); + break; + case LNEG: + verifyAssignable(Type.LONG, simplePeek(frame)); + break; + case FNEG: + verifyAssignable(Type.FLOAT, simplePeek(frame)); + break; + case DNEG: + verifyAssignable(Type.DOUBLE, simplePeek(frame)); + break; + + // Shifts + case ISHL: + evalShift(Type.INTEGER, frame); + break; + case LSHL: + evalShift(Type.LONG, frame); + break; + case ISHR: + evalShift(Type.INTEGER, frame); + break; + case LSHR: + evalShift(Type.LONG, frame); + break; + case IUSHR: + evalShift(Type.INTEGER,frame); + break; + case LUSHR: + evalShift(Type.LONG, frame); + break; + + // Bitwise Math + case IAND: + evalBinaryMath(Type.INTEGER, frame); + break; + case LAND: + evalBinaryMath(Type.LONG, frame); + break; + case IOR: + evalBinaryMath(Type.INTEGER, frame); + break; + case LOR: + evalBinaryMath(Type.LONG, frame); + break; + case IXOR: + evalBinaryMath(Type.INTEGER, frame); + break; + case LXOR: + evalBinaryMath(Type.LONG, frame); + break; + + case IINC: { + int index = iter.byteAt(pos + 1); + verifyAssignable(Type.INTEGER, frame.getLocal(index)); + access(index, Type.INTEGER, subroutine); + break; + } + + // Conversion + case I2L: + verifyAssignable(Type.INTEGER, simplePop(frame)); + simplePush(Type.LONG, frame); + break; + case I2F: + verifyAssignable(Type.INTEGER, simplePop(frame)); + simplePush(Type.FLOAT, frame); + break; + case I2D: + verifyAssignable(Type.INTEGER, simplePop(frame)); + simplePush(Type.DOUBLE, frame); + break; + case L2I: + verifyAssignable(Type.LONG, simplePop(frame)); + simplePush(Type.INTEGER, frame); + break; + case L2F: + verifyAssignable(Type.LONG, simplePop(frame)); + simplePush(Type.FLOAT, frame); + break; + case L2D: + verifyAssignable(Type.LONG, simplePop(frame)); + simplePush(Type.DOUBLE, frame); + break; + case F2I: + verifyAssignable(Type.FLOAT, simplePop(frame)); + simplePush(Type.INTEGER, frame); + break; + case F2L: + verifyAssignable(Type.FLOAT, simplePop(frame)); + simplePush(Type.LONG, frame); + break; + case F2D: + verifyAssignable(Type.FLOAT, simplePop(frame)); + simplePush(Type.DOUBLE, frame); + break; + case D2I: + verifyAssignable(Type.DOUBLE, simplePop(frame)); + simplePush(Type.INTEGER, frame); + break; + case D2L: + verifyAssignable(Type.DOUBLE, simplePop(frame)); + simplePush(Type.LONG, frame); + break; + case D2F: + verifyAssignable(Type.DOUBLE, simplePop(frame)); + simplePush(Type.FLOAT, frame); + break; + case I2B: + case I2C: + case I2S: + verifyAssignable(Type.INTEGER, frame.peek()); + break; + case LCMP: + verifyAssignable(Type.LONG, simplePop(frame)); + verifyAssignable(Type.LONG, simplePop(frame)); + frame.push(Type.INTEGER); + break; + case FCMPL: + case FCMPG: + verifyAssignable(Type.FLOAT, simplePop(frame)); + verifyAssignable(Type.FLOAT, simplePop(frame)); + frame.push(Type.INTEGER); + break; + case DCMPL: + case DCMPG: + verifyAssignable(Type.DOUBLE, simplePop(frame)); + verifyAssignable(Type.DOUBLE, simplePop(frame)); + frame.push(Type.INTEGER); + break; + + // Control flow + case IFEQ: + case IFNE: + case IFLT: + case IFGE: + case IFGT: + case IFLE: + verifyAssignable(Type.INTEGER, simplePop(frame)); + break; + case IF_ICMPEQ: + case IF_ICMPNE: + case IF_ICMPLT: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + verifyAssignable(Type.INTEGER, simplePop(frame)); + verifyAssignable(Type.INTEGER, simplePop(frame)); + break; + case IF_ACMPEQ: + case IF_ACMPNE: + verifyAssignable(Type.OBJECT, simplePop(frame)); + verifyAssignable(Type.OBJECT, simplePop(frame)); + break; + case GOTO: + break; + case JSR: + frame.push(Type.RETURN_ADDRESS); + break; + case RET: + verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(iter.byteAt(pos + 1))); + break; + case TABLESWITCH: + case LOOKUPSWITCH: + case IRETURN: + verifyAssignable(Type.INTEGER, simplePop(frame)); + break; + case LRETURN: + verifyAssignable(Type.LONG, simplePop(frame)); + break; + case FRETURN: + verifyAssignable(Type.FLOAT, simplePop(frame)); + break; + case DRETURN: + verifyAssignable(Type.DOUBLE, simplePop(frame)); + break; + case ARETURN: + try { + CtClass returnType = Descriptor.getReturnType(method.getDescriptor(), classPool); + verifyAssignable(Type.get(returnType), simplePop(frame)); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + break; + case RETURN: + break; + case GETSTATIC: + evalGetField(opcode, iter.u16bitAt(pos + 1), frame); + break; + case PUTSTATIC: + evalPutField(opcode, iter.u16bitAt(pos + 1), frame); + break; + case GETFIELD: + evalGetField(opcode, iter.u16bitAt(pos + 1), frame); + break; + case PUTFIELD: + evalPutField(opcode, iter.u16bitAt(pos + 1), frame); + break; + case INVOKEVIRTUAL: + case INVOKESPECIAL: + case INVOKESTATIC: + evalInvokeMethod(opcode, iter.u16bitAt(pos + 1), frame); + break; + case INVOKEINTERFACE: + evalInvokeIntfMethod(opcode, iter.u16bitAt(pos + 1), frame); + break; + case INVOKEDYNAMIC: + evalInvokeDynamic(opcode, iter.u16bitAt(pos + 1), frame); + break; + case NEW: + frame.push(resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1)))); + break; + case NEWARRAY: + evalNewArray(pos, iter, frame); + break; + case ANEWARRAY: + evalNewObjectArray(pos, iter, frame); + break; + case ARRAYLENGTH: { + Type array = simplePop(frame); + if (! array.isArray() && array != Type.UNINIT) + throw new BadBytecode("Array length passed a non-array [pos = " + pos + "]: " + array); + frame.push(Type.INTEGER); + break; + } + case ATHROW: + verifyAssignable(THROWABLE_TYPE, simplePop(frame)); + break; + case CHECKCAST: + verifyAssignable(Type.OBJECT, simplePop(frame)); + frame.push(typeFromDesc(constPool.getClassInfoByDescriptor(iter.u16bitAt(pos + 1)))); + break; + case INSTANCEOF: + verifyAssignable(Type.OBJECT, simplePop(frame)); + frame.push(Type.INTEGER); + break; + case MONITORENTER: + case MONITOREXIT: + verifyAssignable(Type.OBJECT, simplePop(frame)); + break; + case WIDE: + evalWide(pos, iter, frame, subroutine); + break; + case MULTIANEWARRAY: + evalNewObjectArray(pos, iter, frame); + break; + case IFNULL: + case IFNONNULL: + verifyAssignable(Type.OBJECT, simplePop(frame)); + break; + case GOTO_W: + break; + case JSR_W: + frame.push(Type.RETURN_ADDRESS); + break; + } + } + + private Type zeroExtend(Type type) { + if (type == Type.SHORT || type == Type.BYTE || type == Type.CHAR || type == Type.BOOLEAN) + return Type.INTEGER; + + return type; + } + + private void evalArrayLoad(Type expectedComponent, Frame frame) throws BadBytecode { + Type index = frame.pop(); + Type array = frame.pop(); + + // Special case, an array defined by aconst_null + // TODO - we might need to be more inteligent about this + if (array == Type.UNINIT) { + verifyAssignable(Type.INTEGER, index); + if (expectedComponent == Type.OBJECT) { + simplePush(Type.UNINIT, frame); + } else { + simplePush(expectedComponent, frame); + } + return; + } + + Type component = array.getComponent(); + + if (component == null) + throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component); + + component = zeroExtend(component); + + verifyAssignable(expectedComponent, component); + verifyAssignable(Type.INTEGER, index); + simplePush(component, frame); + } + + private void evalArrayStore(Type expectedComponent, Frame frame) throws BadBytecode { + Type value = simplePop(frame); + Type index = frame.pop(); + Type array = frame.pop(); + + if (array == Type.UNINIT) { + verifyAssignable(Type.INTEGER, index); + return; + } + + Type component = array.getComponent(); + + if (component == null) + throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component); + + component = zeroExtend(component); + + verifyAssignable(expectedComponent, component); + verifyAssignable(Type.INTEGER, index); + + // This intentionally only checks for Object on aastore + // downconverting of an array (no casts) + // e.g. Object[] blah = new String[]; + // blah[2] = (Object) "test"; + // blah[3] = new Integer(); // compiler doesnt catch it (has legal bytecode), + // // but will throw arraystoreexception + if (expectedComponent == Type.OBJECT) { + verifyAssignable(expectedComponent, value); + } else { + verifyAssignable(component, value); + } + } + + private void evalBinaryMath(Type expected, Frame frame) throws BadBytecode { + Type value2 = simplePop(frame); + Type value1 = simplePop(frame); + + verifyAssignable(expected, value2); + verifyAssignable(expected, value1); + simplePush(value1, frame); + } + + private void evalGetField(int opcode, int index, Frame frame) throws BadBytecode { + String desc = constPool.getFieldrefType(index); + Type type = zeroExtend(typeFromDesc(desc)); + + if (opcode == GETFIELD) { + Type objectType = resolveClassInfo(constPool.getFieldrefClassName(index)); + verifyAssignable(objectType, simplePop(frame)); + } + + simplePush(type, frame); + } + + private void evalInvokeIntfMethod(int opcode, int index, Frame frame) throws BadBytecode { + String desc = constPool.getInterfaceMethodrefType(index); + Type[] types = paramTypesFromDesc(desc); + int i = types.length; + + while (i > 0) + verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); + + String classInfo = constPool.getInterfaceMethodrefClassName(index); + Type objectType = resolveClassInfo(classInfo); + verifyAssignable(objectType, simplePop(frame)); + + Type returnType = returnTypeFromDesc(desc); + if (returnType != Type.VOID) + simplePush(zeroExtend(returnType), frame); + } + + private void evalInvokeMethod(int opcode, int index, Frame frame) throws BadBytecode { + String desc = constPool.getMethodrefType(index); + Type[] types = paramTypesFromDesc(desc); + int i = types.length; + + while (i > 0) + verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); + + if (opcode != INVOKESTATIC) { + Type objectType = resolveClassInfo(constPool.getMethodrefClassName(index)); + verifyAssignable(objectType, simplePop(frame)); + } + + Type returnType = returnTypeFromDesc(desc); + if (returnType != Type.VOID) + simplePush(zeroExtend(returnType), frame); + } + + private void evalInvokeDynamic(int opcode, int index, Frame frame) throws BadBytecode { + String desc = constPool.getInvokeDynamicType(index); + Type[] types = paramTypesFromDesc(desc); + int i = types.length; + + while (i > 0) + verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); + + // simplePop(frame); // assume CosntPool#REF_invokeStatic + + Type returnType = returnTypeFromDesc(desc); + if (returnType != Type.VOID) + simplePush(zeroExtend(returnType), frame); + } + + private void evalLDC(int index, Frame frame) throws BadBytecode { + int tag = constPool.getTag(index); + Type type; + switch (tag) { + case ConstPool.CONST_String: + type = STRING_TYPE; + break; + case ConstPool.CONST_Integer: + type = Type.INTEGER; + break; + case ConstPool.CONST_Float: + type = Type.FLOAT; + break; + case ConstPool.CONST_Long: + type = Type.LONG; + break; + case ConstPool.CONST_Double: + type = Type.DOUBLE; + break; + case ConstPool.CONST_Class: + type = CLASS_TYPE; + break; + default: + throw new BadBytecode("bad LDC [pos = " + lastPos + "]: " + tag); + } + + simplePush(type, frame); + } + + private void evalLoad(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode { + Type type = frame.getLocal(index); + + verifyAssignable(expected, type); + + simplePush(type, frame); + access(index, type, subroutine); + } + + private void evalNewArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode { + verifyAssignable(Type.INTEGER, simplePop(frame)); + Type type = null; + int typeInfo = iter.byteAt(pos + 1); + switch (typeInfo) { + case T_BOOLEAN: + type = getType("boolean[]"); + break; + case T_CHAR: + type = getType("char[]"); + break; + case T_BYTE: + type = getType("byte[]"); + break; + case T_SHORT: + type = getType("short[]"); + break; + case T_INT: + type = getType("int[]"); + break; + case T_LONG: + type = getType("long[]"); + break; + case T_FLOAT: + type = getType("float[]"); + break; + case T_DOUBLE: + type = getType("double[]"); + break; + default: + throw new BadBytecode("Invalid array type [pos = " + pos + "]: " + typeInfo); + + } + + frame.push(type); + } + + private void evalNewObjectArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode { + // Convert to x[] format + Type type = resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1))); + String name = type.getCtClass().getName(); + int opcode = iter.byteAt(pos); + int dimensions; + + if (opcode == MULTIANEWARRAY) { + dimensions = iter.byteAt(pos + 3); + } else { + name = name + "[]"; + dimensions = 1; + } + + while (dimensions-- > 0) { + verifyAssignable(Type.INTEGER, simplePop(frame)); + } + + simplePush(getType(name), frame); + } + + private void evalPutField(int opcode, int index, Frame frame) throws BadBytecode { + String desc = constPool.getFieldrefType(index); + Type type = zeroExtend(typeFromDesc(desc)); + + verifyAssignable(type, simplePop(frame)); + + if (opcode == PUTFIELD) { + Type objectType = resolveClassInfo(constPool.getFieldrefClassName(index)); + verifyAssignable(objectType, simplePop(frame)); + } + } + + private void evalShift(Type expected, Frame frame) throws BadBytecode { + Type value2 = simplePop(frame); + Type value1 = simplePop(frame); + + verifyAssignable(Type.INTEGER, value2); + verifyAssignable(expected, value1); + simplePush(value1, frame); + } + + private void evalStore(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode { + Type type = simplePop(frame); + + // RETURN_ADDRESS is allowed by ASTORE + if (! (expected == Type.OBJECT && type == Type.RETURN_ADDRESS)) + verifyAssignable(expected, type); + simpleSetLocal(index, type, frame); + access(index, type, subroutine); + } + + private void evalWide(int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode { + int opcode = iter.byteAt(pos + 1); + int index = iter.u16bitAt(pos + 2); + switch (opcode) { + case ILOAD: + evalLoad(Type.INTEGER, index, frame, subroutine); + break; + case LLOAD: + evalLoad(Type.LONG, index, frame, subroutine); + break; + case FLOAD: + evalLoad(Type.FLOAT, index, frame, subroutine); + break; + case DLOAD: + evalLoad(Type.DOUBLE, index, frame, subroutine); + break; + case ALOAD: + evalLoad(Type.OBJECT, index, frame, subroutine); + break; + case ISTORE: + evalStore(Type.INTEGER, index, frame, subroutine); + break; + case LSTORE: + evalStore(Type.LONG, index, frame, subroutine); + break; + case FSTORE: + evalStore(Type.FLOAT, index, frame, subroutine); + break; + case DSTORE: + evalStore(Type.DOUBLE, index, frame, subroutine); + break; + case ASTORE: + evalStore(Type.OBJECT, index, frame, subroutine); + break; + case IINC: + verifyAssignable(Type.INTEGER, frame.getLocal(index)); + break; + case RET: + verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(index)); + break; + default: + throw new BadBytecode("Invalid WIDE operand [pos = " + pos + "]: " + opcode); + } + + } + + private Type getType(String name) throws BadBytecode { + try { + return Type.get(classPool.get(name)); + } catch (NotFoundException e) { + throw new BadBytecode("Could not find class [pos = " + lastPos + "]: " + name); + } + } + + private Type[] paramTypesFromDesc(String desc) throws BadBytecode { + CtClass classes[] = null; + try { + classes = Descriptor.getParameterTypes(desc, classPool); + } catch (NotFoundException e) { + throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); + } + + if (classes == null) + throw new BadBytecode("Could not obtain parameters for descriptor [pos = " + lastPos + "]: " + desc); + + Type[] types = new Type[classes.length]; + for (int i = 0; i < types.length; i++) + types[i] = Type.get(classes[i]); + + return types; + } + + private Type returnTypeFromDesc(String desc) throws BadBytecode { + CtClass clazz = null; + try { + clazz = Descriptor.getReturnType(desc, classPool); + } catch (NotFoundException e) { + throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); + } + + if (clazz == null) + throw new BadBytecode("Could not obtain return type for descriptor [pos = " + lastPos + "]: " + desc); + + return Type.get(clazz); + } + + private Type simplePeek(Frame frame) { + Type type = frame.peek(); + return (type == Type.TOP) ? frame.getStack(frame.getTopIndex() - 1) : type; + } + + private Type simplePop(Frame frame) { + Type type = frame.pop(); + return (type == Type.TOP) ? frame.pop() : type; + } + + private void simplePush(Type type, Frame frame) { + frame.push(type); + if (type.getSize() == 2) + frame.push(Type.TOP); + } + + private void access(int index, Type type, Subroutine subroutine) { + if (subroutine == null) + return; + subroutine.access(index); + if (type.getSize() == 2) + subroutine.access(index + 1); + } + + private void simpleSetLocal(int index, Type type, Frame frame) { + frame.setLocal(index, type); + if (type.getSize() == 2) + frame.setLocal(index + 1, Type.TOP); + } + + private Type resolveClassInfo(String info) throws BadBytecode { + CtClass clazz = null; + try { + if (info.charAt(0) == '[') { + clazz = Descriptor.toCtClass(info, classPool); + } else { + clazz = classPool.get(info); + } + + } catch (NotFoundException e) { + throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); + } + + if (clazz == null) + throw new BadBytecode("Could not obtain type for descriptor [pos = " + lastPos + "]: " + info); + + return Type.get(clazz); + } + + private Type typeFromDesc(String desc) throws BadBytecode { + CtClass clazz = null; + try { + clazz = Descriptor.toCtClass(desc, classPool); + } catch (NotFoundException e) { + throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); + } + + if (clazz == null) + throw new BadBytecode("Could not obtain type for descriptor [pos = " + lastPos + "]: " + desc); + + return Type.get(clazz); + } + + private void verifyAssignable(Type expected, Type type) throws BadBytecode { + if (! expected.isAssignableFrom(type)) + throw new BadBytecode("Expected type: " + expected + " Got: " + type + " [pos = " + lastPos + "]"); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java new file mode 100644 index 0000000..10e87db --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java @@ -0,0 +1,289 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + + +/** + * Represents the stack frame and local variable table at a particular point in time. + * + * @author Jason T. Greene + */ +public class Frame { + private Type[] locals; + private Type[] stack; + private int top; + private boolean jsrMerged; + private boolean retMerged; + + /** + * Create a new frame with the specified local variable table size, and max stack size + * + * @param locals the number of local variable table entries + * @param stack the maximum stack size + */ + public Frame(int locals, int stack) { + this.locals = new Type[locals]; + this.stack = new Type[stack]; + } + + /** + * Returns the local varaible table entry at index. + * + * @param index the position in the table + * @return the type if one exists, or null if the position is empty + */ + public Type getLocal(int index) { + return locals[index]; + } + + /** + * Sets the local variable table entry at index to a type. + * + * @param index the position in the table + * @param type the type to set at the position + */ + public void setLocal(int index, Type type) { + locals[index] = type; + } + + + /** + * Returns the type on the stack at the specified index. + * + * @param index the position on the stack + * @return the type of the stack position + */ + public Type getStack(int index) { + return stack[index]; + } + + /** + * Sets the type of the stack position + * + * @param index the position on the stack + * @param type the type to set + */ + public void setStack(int index, Type type) { + stack[index] = type; + } + + /** + * Empties the stack + */ + public void clearStack() { + top = 0; + } + + /** + * Gets the index of the type sitting at the top of the stack. + * This is not to be confused with a length operation which + * would return the number of elements, not the position of + * the last element. + * + * @return the position of the element at the top of the stack + */ + public int getTopIndex() { + return top - 1; + } + + /** + * Returns the number of local variable table entries, specified + * at construction. + * + * @return the number of local variable table entries + */ + public int localsLength() { + return locals.length; + } + + /** + * Gets the top of the stack without altering it + * + * @return the top of the stack + */ + public Type peek() { + if (top < 1) + throw new IndexOutOfBoundsException("Stack is empty"); + + return stack[top - 1]; + } + + /** + * Alters the stack to contain one less element and return it. + * + * @return the element popped from the stack + */ + public Type pop() { + if (top < 1) + throw new IndexOutOfBoundsException("Stack is empty"); + return stack[--top]; + } + + /** + * Alters the stack by placing the passed type on the top + * + * @param type the type to add to the top + */ + public void push(Type type) { + stack[top++] = type; + } + + + /** + * Makes a shallow copy of this frame, i.e. the type instances will + * remain the same. + * + * @return the shallow copy + */ + public Frame copy() { + Frame frame = new Frame(locals.length, stack.length); + System.arraycopy(locals, 0, frame.locals, 0, locals.length); + System.arraycopy(stack, 0, frame.stack, 0, stack.length); + frame.top = top; + return frame; + } + + /** + * Makes a shallow copy of the stack portion of this frame. The local + * variable table size will be copied, but its contents will be empty. + * + * @return the shallow copy of the stack + */ + public Frame copyStack() { + Frame frame = new Frame(locals.length, stack.length); + System.arraycopy(stack, 0, frame.stack, 0, stack.length); + frame.top = top; + return frame; + } + + /** + * Merges all types on the stack of this frame instance with that of the specified frame. + * The local variable table is left untouched. + * + * @param frame the frame to merge the stack from + * @return true if any changes where made + */ + public boolean mergeStack(Frame frame) { + boolean changed = false; + if (top != frame.top) + throw new RuntimeException("Operand stacks could not be merged, they are different sizes!"); + + for (int i = 0; i < top; i++) { + if (stack[i] != null) { + Type prev = stack[i]; + Type merged = prev.merge(frame.stack[i]); + if (merged == Type.BOGUS) + throw new RuntimeException("Operand stacks could not be merged due to differing primitive types: pos = " + i); + + stack[i] = merged; + // always replace the instance in case a multi-interface type changes to a normal Type + if ((! merged.equals(prev)) || merged.popChanged()) { + changed = true; + } + } + } + + return changed; + } + + /** + * Merges all types on the stack and local variable table of this frame with that of the specified + * type. + * + * @param frame the frame to merge with + * @return true if any changes to this frame where made by this merge + */ + public boolean merge(Frame frame) { + boolean changed = false; + + // Local variable table + for (int i = 0; i < locals.length; i++) { + if (locals[i] != null) { + Type prev = locals[i]; + Type merged = prev.merge(frame.locals[i]); + // always replace the instance in case a multi-interface type changes to a normal Type + locals[i] = merged; + if (! merged.equals(prev) || merged.popChanged()) { + changed = true; + } + } else if (frame.locals[i] != null) { + locals[i] = frame.locals[i]; + changed = true; + } + } + + changed |= mergeStack(frame); + return changed; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append("locals = ["); + for (int i = 0; i < locals.length; i++) { + buffer.append(locals[i] == null ? "empty" : locals[i].toString()); + if (i < locals.length - 1) + buffer.append(", "); + } + buffer.append("] stack = ["); + for (int i = 0; i < top; i++) { + buffer.append(stack[i]); + if (i < top - 1) + buffer.append(", "); + } + buffer.append("]"); + + return buffer.toString(); + } + + /** + * Whether or not state from the source JSR instruction has been merged + * + * @return true if JSR state has been merged + */ + boolean isJsrMerged() { + return jsrMerged; + } + + /** + * Sets whether of not the state from the source JSR instruction has been merged + * + * @param jsrMerged true if merged, otherwise false + */ + void setJsrMerged(boolean jsrMerged) { + this.jsrMerged = jsrMerged; + } + + /** + * Whether or not state from the RET instruction, of the subroutine that was jumped + * to has been merged. + * + * @return true if RET state has been merged + */ + boolean isRetMerged() { + return retMerged; + } + + /** + * Sets whether or not state from the RET instruction, of the subroutine that was jumped + * to has been merged. + * + * @param retMerged true if RET state has been merged + */ + void setRetMerged(boolean retMerged) { + this.retMerged = retMerged; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java new file mode 100644 index 0000000..84bd21c --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java @@ -0,0 +1,148 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import java.io.PrintStream; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.Modifier; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.CodeAttribute; +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.bytecode.InstructionPrinter; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; + +/** + * A utility class for printing a merged view of the frame state and the + * instructions of a method. + * + * @author Jason T. Greene + */ +public final class FramePrinter { + private final PrintStream stream; + + /** + * Constructs a bytecode printer. + */ + public FramePrinter(PrintStream stream) { + this.stream = stream; + } + + /** + * Prints all the methods declared in the given class. + */ + public static void print(CtClass clazz, PrintStream stream) { + (new FramePrinter(stream)).print(clazz); + } + + /** + * Prints all the methods declared in the given class. + */ + public void print(CtClass clazz) { + CtMethod[] methods = clazz.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + print(methods[i]); + } + } + + private String getMethodString(CtMethod method) { + try { + return Modifier.toString(method.getModifiers()) + " " + + method.getReturnType().getName() + " " + method.getName() + + Descriptor.toString(method.getSignature()) + ";"; + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Prints the instructions and the frame states of the given method. + */ + public void print(CtMethod method) { + stream.println("\n" + getMethodString(method)); + MethodInfo info = method.getMethodInfo2(); + ConstPool pool = info.getConstPool(); + CodeAttribute code = info.getCodeAttribute(); + if (code == null) + return; + + Frame[] frames; + try { + frames = (new Analyzer()).analyze(method.getDeclaringClass(), info); + } catch (BadBytecode e) { + throw new RuntimeException(e); + } + + int spacing = String.valueOf(code.getCodeLength()).length(); + + CodeIterator iterator = code.iterator(); + while (iterator.hasNext()) { + int pos; + try { + pos = iterator.next(); + } catch (BadBytecode e) { + throw new RuntimeException(e); + } + + stream.println(pos + ": " + InstructionPrinter.instructionString(iterator, pos, pool)); + + addSpacing(spacing + 3); + Frame frame = frames[pos]; + if (frame == null) { + stream.println("--DEAD CODE--"); + continue; + } + printStack(frame); + + addSpacing(spacing + 3); + printLocals(frame); + } + + } + + private void printStack(Frame frame) { + stream.print("stack ["); + int top = frame.getTopIndex(); + for (int i = 0; i <= top; i++) { + if (i > 0) + stream.print(", "); + Type type = frame.getStack(i); + stream.print(type); + } + stream.println("]"); + } + + private void printLocals(Frame frame) { + stream.print("locals ["); + int length = frame.localsLength(); + for (int i = 0; i < length; i++) { + if (i > 0) + stream.print(", "); + Type type = frame.getLocal(i); + stream.print(type == null ? "empty" : type.toString()); + } + stream.println("]"); + } + + private void addSpacing(int count) { + while (count-- > 0) + stream.print(' '); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java new file mode 100644 index 0000000..52e3b53 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java @@ -0,0 +1,57 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import java.util.NoSuchElementException; + +class IntQueue { + private static class Entry { + private IntQueue.Entry next; + private int value; + private Entry(int value) { + this.value = value; + } + } + private IntQueue.Entry head; + + private IntQueue.Entry tail; + + void add(int value) { + IntQueue.Entry entry = new Entry(value); + if (tail != null) + tail.next = entry; + tail = entry; + + if (head == null) + head = entry; + } + + boolean isEmpty() { + return head == null; + } + + int take() { + if (head == null) + throw new NoSuchElementException(); + + int value = head.value; + head = head.next; + if (head == null) + tail = null; + + return value; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java new file mode 100644 index 0000000..8a9eefa --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java @@ -0,0 +1,130 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.NotFoundException; + +/** + * Represents an array of {@link MultiType} instances. + * + * @author Jason T. Greene + */ +public class MultiArrayType extends Type { + private MultiType component; + private int dims; + + public MultiArrayType(MultiType component, int dims) { + super(null); + this.component = component; + this.dims = dims; + } + + public CtClass getCtClass() { + CtClass clazz = component.getCtClass(); + if (clazz == null) + return null; + + ClassPool pool = clazz.getClassPool(); + if (pool == null) + pool = ClassPool.getDefault(); + + String name = arrayName(clazz.getName(), dims); + + try { + return pool.get(name); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + } + + boolean popChanged() { + return component.popChanged(); + } + + public int getDimensions() { + return dims; + } + + public Type getComponent() { + return dims == 1 ? (Type)component : new MultiArrayType(component, dims - 1); + } + + public int getSize() { + return 1; + } + + public boolean isArray() { + return true; + } + + public boolean isAssignableFrom(Type type) { + throw new UnsupportedOperationException("Not implemented"); + } + + public boolean isReference() { + return true; + } + + public boolean isAssignableTo(Type type) { + if (eq(type.getCtClass(), Type.OBJECT.getCtClass())) + return true; + + if (eq(type.getCtClass(), Type.CLONEABLE.getCtClass())) + return true; + + if (eq(type.getCtClass(), Type.SERIALIZABLE.getCtClass())) + return true; + + if (! type.isArray()) + return false; + + Type typeRoot = getRootComponent(type); + int typeDims = type.getDimensions(); + + if (typeDims > dims) + return false; + + if (typeDims < dims) { + if (eq(typeRoot.getCtClass(), Type.OBJECT.getCtClass())) + return true; + + if (eq(typeRoot.getCtClass(), Type.CLONEABLE.getCtClass())) + return true; + + if (eq(typeRoot.getCtClass(), Type.SERIALIZABLE.getCtClass())) + return true; + + return false; + } + + return component.isAssignableTo(typeRoot); + } + + public boolean equals(Object o) { + if (! (o instanceof MultiArrayType)) + return false; + MultiArrayType multi = (MultiArrayType)o; + + return component.equals(multi.component) && dims == multi.dims; + } + + public String toString() { + // follows the same detailed formating scheme as component + return arrayName(component.toString(), dims); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java new file mode 100644 index 0000000..6158535 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java @@ -0,0 +1,314 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.wenshuo.agent.javassist.CtClass; + +/** + * MultiType represents an unresolved type. Whenever two {@code Type} + * instances are merged, if they share more than one super type (either an + * interface or a superclass), then a {@code MultiType} is used to + * represent the possible super types. The goal of a {@code MultiType} + * is to reduce the set of possible types down to a single resolved type. This + * is done by eliminating non-assignable types from the typeset when the + * {@code MultiType} is passed as an argument to + * {@link Type#isAssignableFrom(Type)}, as well as removing non-intersecting + * types during a merge. + * + * Note: Currently the {@code MultiType} instance is reused as much + * as possible so that updates are visible from all frames. In addition, all + * {@code MultiType} merge paths are also updated. This is somewhat + * hackish, but it appears to handle most scenarios. + * + * @author Jason T. Greene + */ + +/* TODO - A better, but more involved, approach would be to track the instruction + * offset that resulted in the creation of this type, and + * whenever the typeset changes, to force a merge on that position. This + * would require creating a new MultiType instance every time the typeset + * changes, and somehow communicating assignment changes to the Analyzer + */ +public class MultiType extends Type { + private Map interfaces; + private Type resolved; + private Type potentialClass; + private MultiType mergeSource; + private boolean changed = false; + + public MultiType(Map interfaces) { + this(interfaces, null); + } + + public MultiType(Map interfaces, Type potentialClass) { + super(null); + this.interfaces = interfaces; + this.potentialClass = potentialClass; + } + + /** + * Gets the class that corresponds with this type. If this information + * is not yet known, java.lang.Object will be returned. + */ + public CtClass getCtClass() { + if (resolved != null) + return resolved.getCtClass(); + + return Type.OBJECT.getCtClass(); + } + + /** + * Always returns null since this type is never used for an array. + */ + public Type getComponent() { + return null; + } + + /** + * Always returns 1, since this type is a reference. + */ + public int getSize() { + return 1; + } + + /** + * Always reutnrs false since this type is never used for an array + */ + public boolean isArray() { + return false; + } + + /** + * Returns true if the internal state has changed. + */ + boolean popChanged() { + boolean changed = this.changed; + this.changed = false; + return changed; + } + + public boolean isAssignableFrom(Type type) { + throw new UnsupportedOperationException("Not implemented"); + } + + public boolean isAssignableTo(Type type) { + if (resolved != null) + return type.isAssignableFrom(resolved); + + if (Type.OBJECT.equals(type)) + return true; + + if (potentialClass != null && !type.isAssignableFrom(potentialClass)) + potentialClass = null; + + Map map = mergeMultiAndSingle(this, type); + + if (map.size() == 1 && potentialClass == null) { + // Update previous merge paths to the same resolved type + resolved = Type.get((CtClass)map.values().iterator().next()); + propogateResolved(); + + return true; + } + + // Keep all previous merge paths up to date + if (map.size() >= 1) { + interfaces = map; + propogateState(); + + return true; + } + + if (potentialClass != null) { + resolved = potentialClass; + propogateResolved(); + + return true; + } + + return false; + } + + private void propogateState() { + MultiType source = mergeSource; + while (source != null) { + source.interfaces = interfaces; + source.potentialClass = potentialClass; + source = source.mergeSource; + } + } + + private void propogateResolved() { + MultiType source = mergeSource; + while (source != null) { + source.resolved = resolved; + source = source.mergeSource; + } + } + + /** + * Always returns true, since this type is always a reference. + * + * @return true + */ + public boolean isReference() { + return true; + } + + private Map getAllMultiInterfaces(MultiType type) { + Map map = new HashMap(); + + Iterator iter = type.interfaces.values().iterator(); + while (iter.hasNext()) { + CtClass intf = (CtClass)iter.next(); + map.put(intf.getName(), intf); + getAllInterfaces(intf, map); + } + + return map; + } + + + private Map mergeMultiInterfaces(MultiType type1, MultiType type2) { + Map map1 = getAllMultiInterfaces(type1); + Map map2 = getAllMultiInterfaces(type2); + + return findCommonInterfaces(map1, map2); + } + + private Map mergeMultiAndSingle(MultiType multi, Type single) { + Map map1 = getAllMultiInterfaces(multi); + Map map2 = getAllInterfaces(single.getCtClass(), null); + + return findCommonInterfaces(map1, map2); + } + + private boolean inMergeSource(MultiType source) { + while (source != null) { + if (source == this) + return true; + + source = source.mergeSource; + } + + return false; + } + + public Type merge(Type type) { + if (this == type) + return this; + + if (type == UNINIT) + return this; + + if (type == BOGUS) + return BOGUS; + + if (type == null) + return this; + + if (resolved != null) + return resolved.merge(type); + + if (potentialClass != null) { + Type mergePotential = potentialClass.merge(type); + if (! mergePotential.equals(potentialClass) || mergePotential.popChanged()) { + potentialClass = Type.OBJECT.equals(mergePotential) ? null : mergePotential; + changed = true; + } + } + + Map merged; + + if (type instanceof MultiType) { + MultiType multi = (MultiType)type; + + if (multi.resolved != null) { + merged = mergeMultiAndSingle(this, multi.resolved); + } else { + merged = mergeMultiInterfaces(multi, this); + if (! inMergeSource(multi)) + mergeSource = multi; + } + } else { + merged = mergeMultiAndSingle(this, type); + } + + // Keep all previous merge paths up to date + if (merged.size() > 1 || (merged.size() == 1 && potentialClass != null)) { + // Check for changes + if (merged.size() != interfaces.size()) { + changed = true; + } else if (changed == false){ + Iterator iter = merged.keySet().iterator(); + while (iter.hasNext()) + if (! interfaces.containsKey(iter.next())) + changed = true; + } + + interfaces = merged; + propogateState(); + + return this; + } + + if (merged.size() == 1) { + resolved = Type.get((CtClass) merged.values().iterator().next()); + } else if (potentialClass != null){ + resolved = potentialClass; + } else { + resolved = OBJECT; + } + + propogateResolved(); + + return resolved; + } + + public boolean equals(Object o) { + if (! (o instanceof MultiType)) + return false; + + MultiType multi = (MultiType) o; + if (resolved != null) + return resolved.equals(multi.resolved); + else if (multi.resolved != null) + return false; + + return interfaces.keySet().equals(multi.interfaces.keySet()); + } + + public String toString() { + if (resolved != null) + return resolved.toString(); + + StringBuffer buffer = new StringBuffer("{"); + Iterator iter = interfaces.keySet().iterator(); + while (iter.hasNext()) { + buffer.append(iter.next()); + buffer.append(", "); + } + buffer.setLength(buffer.length() - 2); + if (potentialClass != null) + buffer.append(", *").append(potentialClass.toString()); + buffer.append("}"); + return buffer.toString(); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java new file mode 100644 index 0000000..2f6f254 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java @@ -0,0 +1,67 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Represents a nested method subroutine (marked by JSR and RET). + * + * @author Jason T. Greene + */ +public class Subroutine { + //private Set callers = new HashSet(); + private List callers = new ArrayList(); + private Set access = new HashSet(); + private int start; + + public Subroutine(int start, int caller) { + this.start = start; + callers.add(new Integer(caller)); + } + + public void addCaller(int caller) { + callers.add(new Integer(caller)); + } + + public int start() { + return start; + } + + public void access(int index) { + access.add(new Integer(index)); + } + + public boolean isAccessed(int index) { + return access.contains(new Integer(index)); + } + + public Collection accessed() { + return access; + } + + public Collection callers() { + return callers; + } + + public String toString() { + return "start = " + start + " callers = " + callers.toString(); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java new file mode 100644 index 0000000..2b43955 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java @@ -0,0 +1,157 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.CodeAttribute; +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.ExceptionTable; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; +import com.wenshuo.agent.javassist.bytecode.Opcode; + +/** + * Discovers the subroutines in a method, and tracks all callers. + * + * @author Jason T. Greene + */ +public class SubroutineScanner implements Opcode { + + private Subroutine[] subroutines; + Map subTable = new HashMap(); + Set done = new HashSet(); + + + public Subroutine[] scan(MethodInfo method) throws BadBytecode { + CodeAttribute code = method.getCodeAttribute(); + CodeIterator iter = code.iterator(); + + subroutines = new Subroutine[code.getCodeLength()]; + subTable.clear(); + done.clear(); + + scan(0, iter, null); + + ExceptionTable exceptions = code.getExceptionTable(); + for (int i = 0; i < exceptions.size(); i++) { + int handler = exceptions.handlerPc(i); + // If an exception is thrown in subroutine, the handler + // is part of the same subroutine. + scan(handler, iter, subroutines[exceptions.startPc(i)]); + } + + return subroutines; + } + + private void scan(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { + // Skip already processed blocks + if (done.contains(new Integer(pos))) + return; + + done.add(new Integer(pos)); + + int old = iter.lookAhead(); + iter.move(pos); + + boolean next; + do { + pos = iter.next(); + next = scanOp(pos, iter, sub) && iter.hasNext(); + } while (next); + + iter.move(old); + } + + private boolean scanOp(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { + subroutines[pos] = sub; + + int opcode = iter.byteAt(pos); + + if (opcode == TABLESWITCH) { + scanTableSwitch(pos, iter, sub); + + return false; + } + + if (opcode == LOOKUPSWITCH) { + scanLookupSwitch(pos, iter, sub); + + return false; + } + + // All forms of return and throw end current code flow + if (Util.isReturn(opcode) || opcode == RET || opcode == ATHROW) + return false; + + if (Util.isJumpInstruction(opcode)) { + int target = Util.getJumpTarget(pos, iter); + if (opcode == JSR || opcode == JSR_W) { + Subroutine s = (Subroutine) subTable.get(new Integer(target)); + if (s == null) { + s = new Subroutine(target, pos); + subTable.put(new Integer(target), s); + scan(target, iter, s); + } else { + s.addCaller(pos); + } + } else { + scan(target, iter, sub); + + // GOTO ends current code flow + if (Util.isGoto(opcode)) + return false; + } + } + + return true; + } + + private void scanLookupSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { + int index = (pos & ~3) + 4; + // default + scan(pos + iter.s32bitAt(index), iter, sub); + int npairs = iter.s32bitAt(index += 4); + int end = npairs * 8 + (index += 4); + + // skip "match" + for (index += 4; index < end; index += 8) { + int target = iter.s32bitAt(index) + pos; + scan(target, iter, sub); + } + } + + private void scanTableSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { + // Skip 4 byte alignment padding + int index = (pos & ~3) + 4; + // default + scan(pos + iter.s32bitAt(index), iter, sub); + int low = iter.s32bitAt(index += 4); + int high = iter.s32bitAt(index += 4); + int end = (high - low + 1) * 4 + (index += 4); + + // Offset table + for (; index < end; index += 4) { + int target = iter.s32bitAt(index) + pos; + scan(target, iter, sub); + } + } + + +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Type.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Type.java new file mode 100644 index 0000000..41b36b3 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Type.java @@ -0,0 +1,593 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.NotFoundException; + +/** + * Represents a JVM type in data-flow analysis. This abstraction is necessary since + * a JVM type not only includes all normal Java types, but also a few special types + * that are used by the JVM internally. See the static field types on this class for + * more info on these special types. + * + * All primitive and special types reuse the same instance, so identity comparison can + * be used when examining them. Normal java types must use {@link #equals(Object)} to + * compare type instances. + * + * In most cases, applications which consume this API, only need to call {@link #getCtClass()} + * to obtain the needed type information. + * + * @author Jason T. Greene + */ +public class Type { + private final CtClass clazz; + private final boolean special; + + private static final Map prims = new IdentityHashMap(); + /** Represents the double primitive type */ + public static final Type DOUBLE = new Type(CtClass.doubleType); + /** Represents the boolean primitive type */ + public static final Type BOOLEAN = new Type(CtClass.booleanType); + /** Represents the long primitive type */ + public static final Type LONG = new Type(CtClass.longType); + /** Represents the char primitive type */ + public static final Type CHAR = new Type(CtClass.charType); + /** Represents the byte primitive type */ + public static final Type BYTE = new Type(CtClass.byteType); + /** Represents the short primitive type */ + public static final Type SHORT = new Type(CtClass.shortType); + /** Represents the integer primitive type */ + public static final Type INTEGER = new Type(CtClass.intType); + /** Represents the float primitive type */ + public static final Type FLOAT = new Type(CtClass.floatType); + /** Represents the void primitive type */ + public static final Type VOID = new Type(CtClass.voidType); + + /** + * Represents an unknown, or null type. This occurs when aconst_null is used. + * It is important not to treat this type as java.lang.Object, since a null can + * be assigned to any reference type. The analyzer will replace these with + * an actual known type if it can be determined by a merged path with known type + * information. If this type is encountered on a frame then it is guaranteed to + * be null, and the type information is simply not available. Any attempts to + * infer the type, without further information from the compiler would be a guess. + */ + public static final Type UNINIT = new Type(null); + + /** + * Represents an internal JVM return address, which is used by the RET + * instruction to return to a JSR that invoked the subroutine. + */ + public static final Type RETURN_ADDRESS = new Type(null, true); + + /** A placeholder used by the analyzer for the second word position of a double-word type */ + public static final Type TOP = new Type(null, true); + + /** + * Represents a non-accessible value. Code cannot access the value this type + * represents. It occurs when bytecode reuses a local variable table + * position with non-mergable types. An example would be compiled code which + * uses the same position for a primitive type in one branch, and a reference type + * in another branch. + */ + public static final Type BOGUS = new Type(null, true); + + /** Represents the java.lang.Object reference type */ + public static final Type OBJECT = lookupType("java.lang.Object"); + /** Represents the java.io.Serializable reference type */ + public static final Type SERIALIZABLE = lookupType("java.io.Serializable"); + /** Represents the java.lang.Coneable reference type */ + public static final Type CLONEABLE = lookupType("java.lang.Cloneable"); + /** Represents the java.lang.Throwable reference type */ + public static final Type THROWABLE = lookupType("java.lang.Throwable"); + + static { + prims.put(CtClass.doubleType, DOUBLE); + prims.put(CtClass.longType, LONG); + prims.put(CtClass.charType, CHAR); + prims.put(CtClass.shortType, SHORT); + prims.put(CtClass.intType, INTEGER); + prims.put(CtClass.floatType, FLOAT); + prims.put(CtClass.byteType, BYTE); + prims.put(CtClass.booleanType, BOOLEAN); + prims.put(CtClass.voidType, VOID); + + } + + /** + * Obtain the Type for a given class. If the class is a primitive, + * the the unique type instance for the primitive will be returned. + * Otherwise a new Type instance representing the class is returned. + * + * @param clazz The java class + * @return a type instance for this class + */ + public static Type get(CtClass clazz) { + Type type = (Type)prims.get(clazz); + return type != null ? type : new Type(clazz); + } + + private static Type lookupType(String name) { + try { + return new Type(ClassPool.getDefault().get(name)); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + } + + Type(CtClass clazz) { + this(clazz, false); + } + + private Type(CtClass clazz, boolean special) { + this.clazz = clazz; + this.special = special; + } + + // Used to indicate a merge internally triggered a change + boolean popChanged() { + return false; + } + + /** + * Gets the word size of this type. Double-word types, such as long and double + * will occupy two positions on the local variable table or stack. + * + * @return the number of words needed to hold this type + */ + public int getSize() { + return clazz == CtClass.doubleType || clazz == CtClass.longType || this == TOP ? 2 : 1; + } + + /** + * Returns the class this type represents. If the type is special, null will be returned. + * + * @return the class for this type, or null if special + */ + public CtClass getCtClass() { + return clazz; + } + + /** + * Returns whether or not this type is a normal java reference, i.e. it is or extends java.lang.Object. + * + * @return true if a java reference, false if a primitive or special + */ + public boolean isReference() { + return !special && (clazz == null || !clazz.isPrimitive()); + } + + /** + * Returns whether or not the type is special. A special type is one that is either used + * for internal tracking, or is only used internally by the JVM. + * + * @return true if special, false if not + */ + public boolean isSpecial() { + return special; + } + + /** + * Returns whether or not this type is an array. + * + * @return true if an array, false if not + */ + public boolean isArray() { + return clazz != null && clazz.isArray(); + } + + /** + * Returns the number of dimensions of this array. If the type is not an + * array zero is returned. + * + * @return zero if not an array, otherwise the number of array dimensions. + */ + public int getDimensions() { + if (!isArray()) return 0; + + String name = clazz.getName(); + int pos = name.length() - 1; + int count = 0; + while (name.charAt(pos) == ']' ) { + pos -= 2; + count++; + } + + return count; + } + + /** + * Returns the array component if this type is an array. If the type + * is not an array null is returned. + * + * @return the array component if an array, otherwise null + */ + public Type getComponent() { + if (this.clazz == null || !this.clazz.isArray()) + return null; + + CtClass component; + try { + component = this.clazz.getComponentType(); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + + Type type = (Type)prims.get(component); + return (type != null) ? type : new Type(component); + } + + /** + * Determines whether this type is assignable, to the passed type. + * A type is assignable to another if it is either the same type, or + * a sub-type. + * + * @param type the type to test assignability to + * @return true if this is assignable to type, otherwise false + */ + public boolean isAssignableFrom(Type type) { + if (this == type) + return true; + + if ((type == UNINIT && isReference()) || this == UNINIT && type.isReference()) + return true; + + if (type instanceof MultiType) + return ((MultiType)type).isAssignableTo(this); + + if (type instanceof MultiArrayType) + return ((MultiArrayType)type).isAssignableTo(this); + + + // Primitives and Special types must be identical + if (clazz == null || clazz.isPrimitive()) + return false; + + try { + return type.clazz.subtypeOf(clazz); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Finds the common base type, or interface which both this and the specified + * type can be assigned. If there is more than one possible answer, then a {@link MultiType}, + * or a {@link MultiArrayType} is returned. Multi-types have special rules, + * and successive merges and assignment tests on them will alter their internal state, + * as well as other multi-types they have been merged with. This method is used by + * the data-flow analyzer to merge the type state from multiple branches. + * + * @param type the type to merge with + * @return the merged type + */ + public Type merge(Type type) { + if (type == this) + return this; + if (type == null) + return this; + if (type == Type.UNINIT) + return this; + if (this == Type.UNINIT) + return type; + + // Unequal primitives and special types can not be merged + if (! type.isReference() || ! this.isReference()) + return BOGUS; + + // Centralize merging of multi-interface types + if (type instanceof MultiType) + return type.merge(this); + + if (type.isArray() && this.isArray()) + return mergeArray(type); + + try { + return mergeClasses(type); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + } + + Type getRootComponent(Type type) { + while (type.isArray()) + type = type.getComponent(); + + return type; + } + + private Type createArray(Type rootComponent, int dims) { + if (rootComponent instanceof MultiType) + return new MultiArrayType((MultiType) rootComponent, dims); + + String name = arrayName(rootComponent.clazz.getName(), dims); + + Type type; + try { + type = Type.get(getClassPool(rootComponent).get(name)); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + + return type; + } + + String arrayName(String component, int dims) { + // Using char[] since we have no StringBuilder in JDK4, and StringBuffer is slow. + // Although, this is more efficient even if we did have one. + int i = component.length(); + int size = i + dims * 2; + char[] string = new char[size]; + component.getChars(0, i, string, 0); + while (i < size) { + string[i++] = '['; + string[i++] = ']'; + } + component = new String(string); + return component; + } + + private ClassPool getClassPool(Type rootComponent) { + ClassPool pool = rootComponent.clazz.getClassPool(); + return pool != null ? pool : ClassPool.getDefault(); + } + + private Type mergeArray(Type type) { + Type typeRoot = getRootComponent(type); + Type thisRoot = getRootComponent(this); + int typeDims = type.getDimensions(); + int thisDims = this.getDimensions(); + + // Array commponents can be merged when the dimensions are equal + if (typeDims == thisDims) { + Type mergedComponent = thisRoot.merge(typeRoot); + + // If the components can not be merged (a primitive component mixed with a different type) + // then Object is the common type. + if (mergedComponent == Type.BOGUS) + return Type.OBJECT; + + return createArray(mergedComponent, thisDims); + } + + Type targetRoot; + int targetDims; + + if (typeDims < thisDims) { + targetRoot = typeRoot; + targetDims = typeDims; + } else { + targetRoot = thisRoot; + targetDims = thisDims; + } + + // Special case, arrays are cloneable and serializable, so prefer them when dimensions differ + if (eq(CLONEABLE.clazz, targetRoot.clazz) || eq(SERIALIZABLE.clazz, targetRoot.clazz)) + return createArray(targetRoot, targetDims); + + return createArray(OBJECT, targetDims); + } + + private static CtClass findCommonSuperClass(CtClass one, CtClass two) throws NotFoundException { + CtClass deep = one; + CtClass shallow = two; + CtClass backupShallow = shallow; + CtClass backupDeep = deep; + + // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly + for (;;) { + // In case we get lucky, and find a match early + if (eq(deep, shallow) && deep.getSuperclass() != null) + return deep; + + CtClass deepSuper = deep.getSuperclass(); + CtClass shallowSuper = shallow.getSuperclass(); + + if (shallowSuper == null) { + // right, now reset shallow + shallow = backupShallow; + break; + } + + if (deepSuper == null) { + // wrong, swap them, since deep is now useless, its our tmp before we swap it + deep = backupDeep; + backupDeep = backupShallow; + backupShallow = deep; + + deep = shallow; + shallow = backupShallow; + break; + } + + deep = deepSuper; + shallow = shallowSuper; + } + + // Phase 2 - Move deepBackup up by (deep end - deep) + for (;;) { + deep = deep.getSuperclass(); + if (deep == null) + break; + + backupDeep = backupDeep.getSuperclass(); + } + + deep = backupDeep; + + // Phase 3 - The hierarchy positions are now aligned + // The common super class is easy to find now + while (!eq(deep, shallow)) { + deep = deep.getSuperclass(); + shallow = shallow.getSuperclass(); + } + + return deep; + } + + private Type mergeClasses(Type type) throws NotFoundException { + CtClass superClass = findCommonSuperClass(this.clazz, type.clazz); + + // If its Object, then try and find a common interface(s) + if (superClass.getSuperclass() == null) { + Map interfaces = findCommonInterfaces(type); + if (interfaces.size() == 1) + return new Type((CtClass) interfaces.values().iterator().next()); + if (interfaces.size() > 1) + return new MultiType(interfaces); + + // Only Object is in common + return new Type(superClass); + } + + // Check for a common interface that is not on the found supertype + Map commonDeclared = findExclusiveDeclaredInterfaces(type, superClass); + if (commonDeclared.size() > 0) { + return new MultiType(commonDeclared, new Type(superClass)); + } + + return new Type(superClass); + } + + private Map findCommonInterfaces(Type type) { + Map typeMap = getAllInterfaces(type.clazz, null); + Map thisMap = getAllInterfaces(this.clazz, null); + + return findCommonInterfaces(typeMap, thisMap); + } + + private Map findExclusiveDeclaredInterfaces(Type type, CtClass exclude) { + Map typeMap = getDeclaredInterfaces(type.clazz, null); + Map thisMap = getDeclaredInterfaces(this.clazz, null); + Map excludeMap = getAllInterfaces(exclude, null); + + Iterator i = excludeMap.keySet().iterator(); + while (i.hasNext()) { + Object intf = i.next(); + typeMap.remove(intf); + thisMap.remove(intf); + } + + return findCommonInterfaces(typeMap, thisMap); + } + + + Map findCommonInterfaces(Map typeMap, Map alterMap) { + Iterator i = alterMap.keySet().iterator(); + while (i.hasNext()) { + if (! typeMap.containsKey(i.next())) + i.remove(); + } + + // Reduce to subinterfaces + // This does not need to be recursive since we make a copy, + // and that copy contains all super types for the whole hierarchy + i = new ArrayList(alterMap.values()).iterator(); + while (i.hasNext()) { + CtClass intf = (CtClass) i.next(); + CtClass[] interfaces; + try { + interfaces = intf.getInterfaces(); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + + for (int c = 0; c < interfaces.length; c++) + alterMap.remove(interfaces[c].getName()); + } + + return alterMap; + } + + Map getAllInterfaces(CtClass clazz, Map map) { + if (map == null) + map = new HashMap(); + + if (clazz.isInterface()) + map.put(clazz.getName(), clazz); + do { + try { + CtClass[] interfaces = clazz.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + CtClass intf = interfaces[i]; + map.put(intf.getName(), intf); + getAllInterfaces(intf, map); + } + + clazz = clazz.getSuperclass(); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + } while (clazz != null); + + return map; + } + + Map getDeclaredInterfaces(CtClass clazz, Map map) { + if (map == null) + map = new HashMap(); + + if (clazz.isInterface()) + map.put(clazz.getName(), clazz); + + CtClass[] interfaces; + try { + interfaces = clazz.getInterfaces(); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + + for (int i = 0; i < interfaces.length; i++) { + CtClass intf = interfaces[i]; + map.put(intf.getName(), intf); + getDeclaredInterfaces(intf, map); + } + + return map; + } + + public boolean equals(Object o) { + if (! (o instanceof Type)) + return false; + + return o.getClass() == getClass() && eq(clazz, ((Type)o).clazz); + } + + static boolean eq(CtClass one, CtClass two) { + return one == two || (one != null && two != null && one.getName().equals(two.getName())); + } + + public String toString() { + if (this == BOGUS) + return "BOGUS"; + if (this == UNINIT) + return "UNINIT"; + if (this == RETURN_ADDRESS) + return "RETURN ADDRESS"; + if (this == TOP) + return "TOP"; + + return clazz == null ? "null" : clazz.getName(); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Util.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Util.java new file mode 100644 index 0000000..e935310 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Util.java @@ -0,0 +1,48 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.analysis; + +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.Opcode; + +/** + * A set of common utility methods. + * + * @author Jason T. Greene + */ +public class Util implements Opcode { + public static int getJumpTarget(int pos, CodeIterator iter) { + int opcode = iter.byteAt(pos); + pos += (opcode == JSR_W || opcode == GOTO_W) ? iter.s32bitAt(pos + 1) : iter.s16bitAt(pos + 1); + return pos; + } + + public static boolean isJumpInstruction(int opcode) { + return (opcode >= IFEQ && opcode <= JSR) || opcode == IFNULL || opcode == IFNONNULL || opcode == JSR_W || opcode == GOTO_W; + } + + public static boolean isGoto(int opcode) { + return opcode == GOTO || opcode == GOTO_W; + } + + public static boolean isJsr(int opcode) { + return opcode == JSR || opcode == JSR_W; + } + + public static boolean isReturn(int opcode) { + return (opcode >= IRETURN && opcode <= RETURN); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/package.html b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/package.html new file mode 100644 index 0000000..ec39a47 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/package.html @@ -0,0 +1,20 @@ + + +Bytecode Analysis API. + +

This package provides an API for performing data-flow analysis on a method's bytecode. +This allows the user to determine the type state of the stack and local variable table +at the start of every instruction. In addition this API can be used to validate +bytecode, find dead bytecode, and identify unnecessary checkcasts. +Look at ControlFlow class first for details. + +

The users of this package must know the specifications of +class file and Java bytecode. For more details, read this book: + +

    Tim Lindholm and Frank Yellin, +"The Java Virtual Machine Specification 2nd Ed.", +Addison-Wesley, 1999. +
+ + + diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java new file mode 100644 index 0000000..fa93f9a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java @@ -0,0 +1,348 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.NotFoundException; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Set; +import java.util.Iterator; + +/** + * The annotation structure. + * + *

An instance of this class is returned by + * getAnnotations() in AnnotationsAttribute + * or in ParameterAnnotationsAttribute. + * + * @see javassist.bytecode.AnnotationsAttribute#getAnnotations() + * @see javassist.bytecode.ParameterAnnotationsAttribute#getAnnotations() + * @see MemberValue + * @see MemberValueVisitor + * @see AnnotationsWriter + * + * @author Bill Burke + * @author Shigeru Chiba + * @author Adrian Brock + */ +public class Annotation { + static class Pair { + int name; + MemberValue value; + } + + ConstPool pool; + int typeIndex; + LinkedHashMap members; // this sould be LinkedHashMap + // but it is not supported by JDK 1.3. + + /** + * Constructs an annotation including no members. A member can be + * later added to the created annotation by addMemberValue(). + * + * @param type the index into the constant pool table. + * the entry at that index must be the + * CONSTANT_Utf8_Info structure + * repreenting the name of the annotation interface type. + * @param cp the constant pool table. + * + * @see #addMemberValue(String, MemberValue) + */ + public Annotation(int type, ConstPool cp) { + pool = cp; + typeIndex = type; + members = null; + } + + /** + * Constructs an annotation including no members. A member can be + * later added to the created annotation by addMemberValue(). + * + * @param typeName the name of the annotation interface type. + * @param cp the constant pool table. + * + * @see #addMemberValue(String, MemberValue) + */ + public Annotation(String typeName, ConstPool cp) { + this(cp.addUtf8Info(Descriptor.of(typeName)), cp); + } + + /** + * Constructs an annotation that can be accessed through the interface + * represented by clazz. The values of the members are + * not specified. + * + * @param cp the constant pool table. + * @param clazz the interface. + * @throws NotFoundException when the clazz is not found + */ + public Annotation(ConstPool cp, CtClass clazz) + throws NotFoundException + { + // todo Enums are not supported right now. + this(cp.addUtf8Info(Descriptor.of(clazz.getName())), cp); + + if (!clazz.isInterface()) + throw new RuntimeException( + "Only interfaces are allowed for Annotation creation."); + + CtMethod methods[] = clazz.getDeclaredMethods(); + if (methods.length > 0) { + members = new LinkedHashMap(); + } + + for (int i = 0; i < methods.length; i++) { + CtClass returnType = methods[i].getReturnType(); + addMemberValue(methods[i].getName(), + createMemberValue(cp, returnType)); + + } + } + + /** + * Makes an instance of MemberValue. + * + * @param cp the constant pool table. + * @param type the type of the member. + * @return the member value + * @throws NotFoundException when the type is not found + */ + public static MemberValue createMemberValue(ConstPool cp, CtClass type) + throws NotFoundException + { + if (type == CtClass.booleanType) + return new BooleanMemberValue(cp); + else if (type == CtClass.byteType) + return new ByteMemberValue(cp); + else if (type == CtClass.charType) + return new CharMemberValue(cp); + else if (type == CtClass.shortType) + return new ShortMemberValue(cp); + else if (type == CtClass.intType) + return new IntegerMemberValue(cp); + else if (type == CtClass.longType) + return new LongMemberValue(cp); + else if (type == CtClass.floatType) + return new FloatMemberValue(cp); + else if (type == CtClass.doubleType) + return new DoubleMemberValue(cp); + else if (type.getName().equals("java.lang.Class")) + return new ClassMemberValue(cp); + else if (type.getName().equals("java.lang.String")) + return new StringMemberValue(cp); + else if (type.isArray()) { + CtClass arrayType = type.getComponentType(); + MemberValue member = createMemberValue(cp, arrayType); + return new ArrayMemberValue(member, cp); + } + else if (type.isInterface()) { + Annotation info = new Annotation(cp, type); + return new AnnotationMemberValue(info, cp); + } + else { + // treat as enum. I know this is not typed, + // but JBoss has an Annotation Compiler for JDK 1.4 + // and I want it to work with that. - Bill Burke + EnumMemberValue emv = new EnumMemberValue(cp); + emv.setType(type.getName()); + return emv; + } + } + + /** + * Adds a new member. + * + * @param nameIndex the index into the constant pool table. + * The entry at that index must be + * a CONSTANT_Utf8_info structure. + * structure representing the member name. + * @param value the member value. + */ + public void addMemberValue(int nameIndex, MemberValue value) { + Pair p = new Pair(); + p.name = nameIndex; + p.value = value; + addMemberValue(p); + } + + /** + * Adds a new member. + * + * @param name the member name. + * @param value the member value. + */ + public void addMemberValue(String name, MemberValue value) { + Pair p = new Pair(); + p.name = pool.addUtf8Info(name); + p.value = value; + if (members == null) + members = new LinkedHashMap(); + + members.put(name, p); + } + + private void addMemberValue(Pair pair) { + String name = pool.getUtf8Info(pair.name); + if (members == null) + members = new LinkedHashMap(); + + members.put(name, pair); + } + + /** + * Returns a string representation of the annotation. + */ + public String toString() { + StringBuffer buf = new StringBuffer("@"); + buf.append(getTypeName()); + if (members != null) { + buf.append("("); + Iterator mit = members.keySet().iterator(); + while (mit.hasNext()) { + String name = (String)mit.next(); + buf.append(name).append("=").append(getMemberValue(name)); + if (mit.hasNext()) + buf.append(", "); + } + buf.append(")"); + } + + return buf.toString(); + } + + /** + * Obtains the name of the annotation type. + * + * @return the type name + */ + public String getTypeName() { + return Descriptor.toClassName(pool.getUtf8Info(typeIndex)); + } + + /** + * Obtains all the member names. + * + * @return null if no members are defined. + */ + public Set getMemberNames() { + if (members == null) + return null; + else + return members.keySet(); + } + + /** + * Obtains the member value with the given name. + * + *

If this annotation does not have a value for the + * specified member, + * this method returns null. It does not return a + * MemberValue with the default value. + * The default value can be obtained from the annotation type. + * + * @param name the member name + * @return null if the member cannot be found or if the value is + * the default value. + * + * @see javassist.bytecode.AnnotationDefaultAttribute + */ + public MemberValue getMemberValue(String name) { + if (members == null) + return null; + else { + Pair p = (Pair)members.get(name); + if (p == null) + return null; + else + return p.value; + } + } + + /** + * Constructs an annotation-type object representing this annotation. + * For example, if this annotation represents @Author, + * this method returns an Author object. + * + * @param cl class loader for loading an annotation type. + * @param cp class pool for obtaining class files. + * @return the annotation + * @throws ClassNotFoundException if the class cannot found. + * @throws NoSuchClassError if the class linkage fails. + */ + public Object toAnnotationType(ClassLoader cl, ClassPool cp) + throws ClassNotFoundException, NoSuchClassError + { + return AnnotationImpl.make(cl, + MemberValue.loadClass(cl, getTypeName()), + cp, this); + } + + /** + * Writes this annotation. + * + * @param writer the output. + * @throws IOException for an error during the write + */ + public void write(AnnotationsWriter writer) throws IOException { + String typeName = pool.getUtf8Info(typeIndex); + if (members == null) { + writer.annotation(typeName, 0); + return; + } + + writer.annotation(typeName, members.size()); + Iterator it = members.values().iterator(); + while (it.hasNext()) { + Pair pair = (Pair)it.next(); + writer.memberValuePair(pair.name); + pair.value.write(writer); + } + } + + /** + * Returns true if the given object represents the same annotation + * as this object. The equality test checks the member values. + */ + public boolean equals(Object obj) { + if (obj == this) + return true; + if (obj == null || obj instanceof Annotation == false) + return false; + + Annotation other = (Annotation) obj; + + if (getTypeName().equals(other.getTypeName()) == false) + return false; + + LinkedHashMap otherMembers = other.members; + if (members == otherMembers) + return true; + else if (members == null) + return otherMembers == null; + else + if (otherMembers == null) + return false; + else + return members.equals(otherMembers); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java new file mode 100644 index 0000000..faf72ad --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java @@ -0,0 +1,305 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.AnnotationDefaultAttribute; +import com.wenshuo.agent.javassist.bytecode.ClassFile; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; + +/** + * Internal-use only. This is a helper class internally used for implementing + * toAnnotationType() in Annotation. + * + * @author Shigeru Chiba + * @author Bill Burke + * @author Adrian Brock + */ +public class AnnotationImpl implements InvocationHandler { + private static final String JDK_ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation"; + private static Method JDK_ANNOTATION_TYPE_METHOD = null; + + private Annotation annotation; + private ClassPool pool; + private ClassLoader classLoader; + private transient Class annotationType; + private transient int cachedHashCode = Integer.MIN_VALUE; + + static { + // Try to resolve the JDK annotation type method + try { + Class clazz = Class.forName(JDK_ANNOTATION_CLASS_NAME); + JDK_ANNOTATION_TYPE_METHOD = clazz.getMethod("annotationType", (Class[])null); + } + catch (Exception ignored) { + // Probably not JDK5+ + } + } + + /** + * Constructs an annotation object. + * + * @param cl class loader for obtaining annotation types. + * @param clazz the annotation type. + * @param cp class pool for containing an annotation + * type (or null). + * @param anon the annotation. + * @return the annotation + */ + public static Object make(ClassLoader cl, Class clazz, ClassPool cp, + Annotation anon) { + AnnotationImpl handler = new AnnotationImpl(anon, cp, cl); + return Proxy.newProxyInstance(cl, new Class[] { clazz }, handler); + } + + private AnnotationImpl(Annotation a, ClassPool cp, ClassLoader loader) { + annotation = a; + pool = cp; + classLoader = loader; + } + + /** + * Obtains the name of the annotation type. + * + * @return the type name + */ + public String getTypeName() { + return annotation.getTypeName(); + } + + /** + * Get the annotation type + * + * @return the annotation class + * @throws NoClassDefFoundError when the class could not loaded + */ + private Class getAnnotationType() { + if (annotationType == null) { + String typeName = annotation.getTypeName(); + try { + annotationType = classLoader.loadClass(typeName); + } + catch (ClassNotFoundException e) { + NoClassDefFoundError error = new NoClassDefFoundError("Error loading annotation class: " + typeName); + error.setStackTrace(e.getStackTrace()); + throw error; + } + } + return annotationType; + } + + /** + * Obtains the internal data structure representing the annotation. + * + * @return the annotation + */ + public Annotation getAnnotation() { + return annotation; + } + + /** + * Executes a method invocation on a proxy instance. + * The implementations of toString(), equals(), + * and hashCode() are directly supplied by the + * AnnotationImpl. The annotationType() method + * is also available on the proxy instance. + */ + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable + { + String name = method.getName(); + if (Object.class == method.getDeclaringClass()) { + if ("equals".equals(name)) { + Object obj = args[0]; + return new Boolean(checkEquals(obj)); + } + else if ("toString".equals(name)) + return annotation.toString(); + else if ("hashCode".equals(name)) + return new Integer(hashCode()); + } + else if ("annotationType".equals(name) + && method.getParameterTypes().length == 0) + return getAnnotationType(); + + MemberValue mv = annotation.getMemberValue(name); + if (mv == null) + return getDefault(name, method); + else + return mv.getValue(classLoader, pool, method); + } + + private Object getDefault(String name, Method method) + throws ClassNotFoundException, RuntimeException + { + String classname = annotation.getTypeName(); + if (pool != null) { + try { + CtClass cc = pool.get(classname); + ClassFile cf = cc.getClassFile2(); + MethodInfo minfo = cf.getMethod(name); + if (minfo != null) { + AnnotationDefaultAttribute ainfo + = (AnnotationDefaultAttribute) + minfo.getAttribute(AnnotationDefaultAttribute.tag); + if (ainfo != null) { + MemberValue mv = ainfo.getDefaultValue(); + return mv.getValue(classLoader, pool, method); + } + } + } + catch (NotFoundException e) { + throw new RuntimeException("cannot find a class file: " + + classname); + } + } + + throw new RuntimeException("no default value: " + classname + "." + + name + "()"); + } + + /** + * Returns a hash code value for this object. + */ + public int hashCode() { + if (cachedHashCode == Integer.MIN_VALUE) { + int hashCode = 0; + + // Load the annotation class + getAnnotationType(); + + Method[] methods = annotationType.getDeclaredMethods(); + for (int i = 0; i < methods.length; ++ i) { + String name = methods[i].getName(); + int valueHashCode = 0; + + // Get the value + MemberValue mv = annotation.getMemberValue(name); + Object value = null; + try { + if (mv != null) + value = mv.getValue(classLoader, pool, methods[i]); + if (value == null) + value = getDefault(name, methods[i]); + } + catch (RuntimeException e) { + throw e; + } + catch (Exception e) { + throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e); + } + + // Calculate the hash code + if (value != null) { + if (value.getClass().isArray()) + valueHashCode = arrayHashCode(value); + else + valueHashCode = value.hashCode(); + } + hashCode += 127 * name.hashCode() ^ valueHashCode; + } + + cachedHashCode = hashCode; + } + return cachedHashCode; + } + + /** + * Check that another annotation equals ourselves. + * + * @param obj the other annotation + * @return the true when equals false otherwise + * @throws Exception for any problem + */ + private boolean checkEquals(Object obj) throws Exception { + if (obj == null) + return false; + + // Optimization when the other is one of ourselves + if (obj instanceof Proxy) { + InvocationHandler ih = Proxy.getInvocationHandler(obj); + if (ih instanceof AnnotationImpl) { + AnnotationImpl other = (AnnotationImpl) ih; + return annotation.equals(other.annotation); + } + } + + Class otherAnnotationType = (Class) JDK_ANNOTATION_TYPE_METHOD.invoke(obj, (Object[])null); + if (getAnnotationType().equals(otherAnnotationType) == false) + return false; + + Method[] methods = annotationType.getDeclaredMethods(); + for (int i = 0; i < methods.length; ++ i) { + String name = methods[i].getName(); + + // Get the value + MemberValue mv = annotation.getMemberValue(name); + Object value = null; + Object otherValue = null; + try { + if (mv != null) + value = mv.getValue(classLoader, pool, methods[i]); + if (value == null) + value = getDefault(name, methods[i]); + otherValue = methods[i].invoke(obj, (Object[])null); + } + catch (RuntimeException e) { + throw e; + } + catch (Exception e) { + throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e); + } + + if (value == null && otherValue != null) + return false; + if (value != null && value.equals(otherValue) == false) + return false; + } + + return true; + } + + /** + * Calculates the hashCode of an array using the same + * algorithm as java.util.Arrays.hashCode() + * + * @param object the object + * @return the hashCode + */ + private static int arrayHashCode(Object object) + { + if (object == null) + return 0; + + int result = 1; + + Object[] array = (Object[]) object; + for (int i = 0; i < array.length; ++i) { + int elementHashCode = 0; + if (array[i] != null) + elementHashCode = array[i].hashCode(); + result = 31 * result + elementHashCode; + } + return result; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java new file mode 100644 index 0000000..09932ec --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java @@ -0,0 +1,96 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Nested annotation. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class AnnotationMemberValue extends MemberValue { + Annotation value; + + /** + * Constructs an annotation member. The initial value is not specified. + */ + public AnnotationMemberValue(ConstPool cp) { + this(null, cp); + } + + /** + * Constructs an annotation member. The initial value is specified by + * the first parameter. + */ + public AnnotationMemberValue(Annotation a, ConstPool cp) { + super('@', cp); + value = a; + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) + throws ClassNotFoundException + { + return AnnotationImpl.make(cl, getType(cl), cp, value); + } + + Class getType(ClassLoader cl) throws ClassNotFoundException { + if (value == null) + throw new ClassNotFoundException("no type specified"); + else + return loadClass(cl, value.getTypeName()); + } + + /** + * Obtains the value. + */ + public Annotation getValue() { + return value; + } + + /** + * Sets the value of this member. + */ + public void setValue(Annotation newValue) { + value = newValue; + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return value.toString(); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.annotationValue(); + value.write(writer); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitAnnotationMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java new file mode 100644 index 0000000..33beb2d --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java @@ -0,0 +1,354 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import java.io.*; + +import com.wenshuo.agent.javassist.bytecode.ByteArray; +import com.wenshuo.agent.javassist.bytecode.ConstPool; + +/** + * A convenience class for constructing a + * ..Annotations_attribute. + * See the source code of the AnnotationsAttribute.Copier class. + * + *

The following code snippet is an example of use of this class: + * + *

+ * ConstPool pool = ...;
+ * output = new ByteArrayOutputStream();
+ * writer = new AnnotationsWriter(output, pool);
+ *
+ * writer.numAnnotations(1);
+ * writer.annotation("Author", 2);
+ * writer.memberValuePair("name");      // element_value_pair
+ * writer.constValueIndex("chiba");
+ * writer.memberValuePair("address");   // element_value_pair
+ * writer.constValueIndex("tokyo");
+ *
+ * writer.close();
+ * byte[] attribute_info = output.toByteArray();
+ * AnnotationsAttribute anno
+ *     = new AnnotationsAttribute(pool, AnnotationsAttribute.visibleTag,
+ *                                attribute_info);
+ * 
+ * + *

The code snippet above generates the annotation attribute + * corresponding to this annotation: + * + *

+ *  @Author(name = "chiba", address = "tokyo")
+ * 
+ * + * @see javassist.bytecode.AnnotationsAttribute + * @see javassist.bytecode.ParameterAnnotationsAttribute + */ +public class AnnotationsWriter { + protected OutputStream output; + private ConstPool pool; + + /** + * Constructs with the given output stream. + * + * @param os the output stream. + * @param cp the constant pool. + */ + public AnnotationsWriter(OutputStream os, ConstPool cp) { + output = os; + pool = cp; + } + + /** + * Obtains the constant pool given to the constructor. + */ + public ConstPool getConstPool() { + return pool; + } + + /** + * Closes the output stream. + * + */ + public void close() throws IOException { + output.close(); + } + + /** + * Writes num_parameters in + * Runtime(In)VisibleParameterAnnotations_attribute. + * This method must be followed by num calls to + * numAnnotations(). + */ + public void numParameters(int num) throws IOException { + output.write(num); + } + + /** + * Writes num_annotations in + * Runtime(In)VisibleAnnotations_attribute. + * This method must be followed by num calls to + * annotation(). + */ + public void numAnnotations(int num) throws IOException { + write16bit(num); + } + + /** + * Writes annotation. + * This method must be followed by numMemberValuePairs + * calls to memberValuePair(). + * + * @param type the annotation interface name. + * @param numMemberValuePairs num_element_value_pairs + * in annotation. + */ + public void annotation(String type, int numMemberValuePairs) + throws IOException + { + annotation(pool.addUtf8Info(type), numMemberValuePairs); + } + + /** + * Writes annotation. + * This method must be followed by numMemberValuePairs + * calls to memberValuePair(). + * + * @param typeIndex type_index in annotation. + * @param numMemberValuePairs num_element_value_pairs + * in annotation. + */ + public void annotation(int typeIndex, int numMemberValuePairs) + throws IOException + { + write16bit(typeIndex); + write16bit(numMemberValuePairs); + } + + /** + * Writes an element of a element_value_pairs array + * in annotation. + * This method must be followed by a + * call to constValueIndex(), enumConstValue(), + * etc. + * + * @param memberName the element name. + */ + public void memberValuePair(String memberName) throws IOException { + memberValuePair(pool.addUtf8Info(memberName)); + } + + /** + * Writes an element of a element_value_pairs array + * in annotation. + * This method must be followed by a + * call to constValueIndex(), enumConstValue(), + * etc. + * + * @param memberNameIndex element_name_index + * in element_value_pairs array. + */ + public void memberValuePair(int memberNameIndex) throws IOException { + write16bit(memberNameIndex); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param value the constant value. + */ + public void constValueIndex(boolean value) throws IOException { + constValueIndex('Z', pool.addIntegerInfo(value ? 1 : 0)); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param value the constant value. + */ + public void constValueIndex(byte value) throws IOException { + constValueIndex('B', pool.addIntegerInfo(value)); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param value the constant value. + */ + public void constValueIndex(char value) throws IOException { + constValueIndex('C', pool.addIntegerInfo(value)); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param value the constant value. + */ + public void constValueIndex(short value) throws IOException { + constValueIndex('S', pool.addIntegerInfo(value)); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param value the constant value. + */ + public void constValueIndex(int value) throws IOException { + constValueIndex('I', pool.addIntegerInfo(value)); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param value the constant value. + */ + public void constValueIndex(long value) throws IOException { + constValueIndex('J', pool.addLongInfo(value)); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param value the constant value. + */ + public void constValueIndex(float value) throws IOException { + constValueIndex('F', pool.addFloatInfo(value)); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param value the constant value. + */ + public void constValueIndex(double value) throws IOException { + constValueIndex('D', pool.addDoubleInfo(value)); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param value the constant value. + */ + public void constValueIndex(String value) throws IOException { + constValueIndex('s', pool.addUtf8Info(value)); + } + + /** + * Writes tag and const_value_index + * in element_value. + * + * @param tag tag in element_value. + * @param index const_value_index + * in element_value. + */ + public void constValueIndex(int tag, int index) + throws IOException + { + output.write(tag); + write16bit(index); + } + + /** + * Writes tag and enum_const_value + * in element_value. + * + * @param typeName the type name of the enum constant. + * @param constName the simple name of the enum constant. + */ + public void enumConstValue(String typeName, String constName) + throws IOException + { + enumConstValue(pool.addUtf8Info(typeName), + pool.addUtf8Info(constName)); + } + + /** + * Writes tag and enum_const_value + * in element_value. + * + * @param typeNameIndex type_name_index + * in element_value. + * @param constNameIndex const_name_index + * in element_value. + */ + public void enumConstValue(int typeNameIndex, int constNameIndex) + throws IOException + { + output.write('e'); + write16bit(typeNameIndex); + write16bit(constNameIndex); + } + + /** + * Writes tag and class_info_index + * in element_value. + * + * @param name the class name. + */ + public void classInfoIndex(String name) throws IOException { + classInfoIndex(pool.addUtf8Info(name)); + } + + /** + * Writes tag and class_info_index + * in element_value. + * + * @param index class_info_index + */ + public void classInfoIndex(int index) throws IOException { + output.write('c'); + write16bit(index); + } + + /** + * Writes tag and annotation_value + * in element_value. + * This method must be followed by a call to annotation(). + */ + public void annotationValue() throws IOException { + output.write('@'); + } + + /** + * Writes tag and array_value + * in element_value. + * This method must be followed by numValues calls + * to constValueIndex(), enumConstValue(), + * etc. + * + * @param numValues num_values + * in array_value. + */ + public void arrayValue(int numValues) throws IOException { + output.write('['); + write16bit(numValues); + } + + protected void write16bit(int value) throws IOException { + byte[] buf = new byte[2]; + ByteArray.write16bit(value, buf, 0); + output.write(buf); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java new file mode 100644 index 0000000..ff0ab0e --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java @@ -0,0 +1,145 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * Array member. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class ArrayMemberValue extends MemberValue { + MemberValue type; + MemberValue[] values; + + /** + * Constructs an array. The initial value or type are not specified. + */ + public ArrayMemberValue(ConstPool cp) { + super('[', cp); + type = null; + values = null; + } + + /** + * Constructs an array. The initial value is not specified. + * + * @param t the type of the array elements. + */ + public ArrayMemberValue(MemberValue t, ConstPool cp) { + super('[', cp); + type = t; + values = null; + } + + Object getValue(ClassLoader cl, ClassPool cp, Method method) + throws ClassNotFoundException + { + if (values == null) + throw new ClassNotFoundException( + "no array elements found: " + method.getName()); + + int size = values.length; + Class clazz; + if (type == null) { + clazz = method.getReturnType().getComponentType(); + if (clazz == null || size > 0) + throw new ClassNotFoundException("broken array type: " + + method.getName()); + } + else + clazz = type.getType(cl); + + Object a = Array.newInstance(clazz, size); + for (int i = 0; i < size; i++) + Array.set(a, i, values[i].getValue(cl, cp, method)); + + return a; + } + + Class getType(ClassLoader cl) throws ClassNotFoundException { + if (type == null) + throw new ClassNotFoundException("no array type specified"); + + Object a = Array.newInstance(type.getType(cl), 0); + return a.getClass(); + } + + /** + * Obtains the type of the elements. + * + * @return null if the type is not specified. + */ + public MemberValue getType() { + return type; + } + + /** + * Obtains the elements of the array. + */ + public MemberValue[] getValue() { + return values; + } + + /** + * Sets the elements of the array. + */ + public void setValue(MemberValue[] elements) { + values = elements; + if (elements != null && elements.length > 0) + type = elements[0]; + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + StringBuffer buf = new StringBuffer("{"); + if (values != null) { + for (int i = 0; i < values.length; i++) { + buf.append(values[i].toString()); + if (i + 1 < values.length) + buf.append(", "); + } + } + + buf.append("}"); + return buf.toString(); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + int num = values.length; + writer.arrayValue(num); + for (int i = 0; i < num; ++i) + values[i].write(writer); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitArrayMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java new file mode 100644 index 0000000..c03c2bb --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java @@ -0,0 +1,103 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Boolean constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class BooleanMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs a boolean constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Integer_info structure. + */ + public BooleanMemberValue(int index, ConstPool cp) { + super('Z', cp); + this.valueIndex = index; + } + + /** + * Constructs a boolean constant value. + * + * @param b the initial value. + */ + public BooleanMemberValue(boolean b, ConstPool cp) { + super('Z', cp); + setValue(b); + } + + /** + * Constructs a boolean constant value. The initial value is false. + */ + public BooleanMemberValue(ConstPool cp) { + super('Z', cp); + setValue(false); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) { + return new Boolean(getValue()); + } + + Class getType(ClassLoader cl) { + return boolean.class; + } + + /** + * Obtains the value of the member. + */ + public boolean getValue() { + return cp.getIntegerInfo(valueIndex) != 0; + } + + /** + * Sets the value of the member. + */ + public void setValue(boolean newValue) { + valueIndex = cp.addIntegerInfo(newValue ? 1 : 0); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return getValue() ? "true" : "false"; + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.constValueIndex(getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitBooleanMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java new file mode 100644 index 0000000..c26d182 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java @@ -0,0 +1,103 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Byte constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class ByteMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs a byte constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Integer_info structure. + */ + public ByteMemberValue(int index, ConstPool cp) { + super('B', cp); + this.valueIndex = index; + } + + /** + * Constructs a byte constant value. + * + * @param b the initial value. + */ + public ByteMemberValue(byte b, ConstPool cp) { + super('B', cp); + setValue(b); + } + + /** + * Constructs a byte constant value. The initial value is 0. + */ + public ByteMemberValue(ConstPool cp) { + super('B', cp); + setValue((byte)0); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) { + return new Byte(getValue()); + } + + Class getType(ClassLoader cl) { + return byte.class; + } + + /** + * Obtains the value of the member. + */ + public byte getValue() { + return (byte)cp.getIntegerInfo(valueIndex); + } + + /** + * Sets the value of the member. + */ + public void setValue(byte newValue) { + valueIndex = cp.addIntegerInfo(newValue); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return Byte.toString(getValue()); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.constValueIndex(getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitByteMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java new file mode 100644 index 0000000..9fe9c4a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java @@ -0,0 +1,104 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Char constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class CharMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs a char constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Integer_info structure. + */ + public CharMemberValue(int index, ConstPool cp) { + super('C', cp); + this.valueIndex = index; + } + + /** + * Constructs a char constant value. + * + * @param c the initial value. + */ + public CharMemberValue(char c, ConstPool cp) { + super('C', cp); + setValue(c); + } + + /** + * Constructs a char constant value. The initial value is '\0'. + */ + public CharMemberValue(ConstPool cp) { + super('C', cp); + setValue('\0'); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) { + return new Character(getValue()); + } + + Class getType(ClassLoader cl) { + return char.class; + } + + /** + * Obtains the value of the member. + */ + public char getValue() { + return (char)cp.getIntegerInfo(valueIndex); + } + + /** + * Sets the value of the member. + */ + public void setValue(char newValue) { + valueIndex = cp.addIntegerInfo(newValue); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return Character.toString(getValue()); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.constValueIndex(getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitCharMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java new file mode 100644 index 0000000..4b3a697 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java @@ -0,0 +1,140 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.bytecode.SignatureAttribute; + +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Class value. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class ClassMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs a class value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Utf8_info structure. + */ + public ClassMemberValue(int index, ConstPool cp) { + super('c', cp); + this.valueIndex = index; + } + + /** + * Constructs a class value. + * + * @param className the initial value. + */ + public ClassMemberValue(String className, ConstPool cp) { + super('c', cp); + setValue(className); + } + + /** + * Constructs a class value. + * The initial value is java.lang.Class. + */ + public ClassMemberValue(ConstPool cp) { + super('c', cp); + setValue("java.lang.Class"); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) + throws ClassNotFoundException { + final String classname = getValue(); + if (classname.equals("void")) + return void.class; + else if (classname.equals("int")) + return int.class; + else if (classname.equals("byte")) + return byte.class; + else if (classname.equals("long")) + return long.class; + else if (classname.equals("double")) + return double.class; + else if (classname.equals("float")) + return float.class; + else if (classname.equals("char")) + return char.class; + else if (classname.equals("short")) + return short.class; + else if (classname.equals("boolean")) + return boolean.class; + else + return loadClass(cl, classname); + } + + Class getType(ClassLoader cl) throws ClassNotFoundException { + return loadClass(cl, "java.lang.Class"); + } + + /** + * Obtains the value of the member. + * + * @return fully-qualified class name. + */ + public String getValue() { + String v = cp.getUtf8Info(valueIndex); + try { + return SignatureAttribute.toTypeSignature(v).jvmTypeName(); + } catch (BadBytecode e) { + throw new RuntimeException(e); + } + } + + /** + * Sets the value of the member. + * + * @param newClassName fully-qualified class name. + */ + public void setValue(String newClassName) { + String setTo = Descriptor.of(newClassName); + valueIndex = cp.addUtf8Info(setTo); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return getValue().replace('$', '.') + ".class"; + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.classInfoIndex(cp.getUtf8Info(valueIndex)); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitClassMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java new file mode 100644 index 0000000..fcfdad0 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java @@ -0,0 +1,105 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Double floating-point number constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + * @version $Revision: 1.7 $ + */ +public class DoubleMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs a double constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Double_info structure. + */ + public DoubleMemberValue(int index, ConstPool cp) { + super('D', cp); + this.valueIndex = index; + } + + /** + * Constructs a double constant value. + * + * @param d the initial value. + */ + public DoubleMemberValue(double d, ConstPool cp) { + super('D', cp); + setValue(d); + } + + /** + * Constructs a double constant value. The initial value is 0.0. + */ + public DoubleMemberValue(ConstPool cp) { + super('D', cp); + setValue(0.0); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) { + return new Double(getValue()); + } + + Class getType(ClassLoader cl) { + return double.class; + } + + /** + * Obtains the value of the member. + */ + public double getValue() { + return cp.getDoubleInfo(valueIndex); + } + + /** + * Sets the value of the member. + */ + public void setValue(double newValue) { + valueIndex = cp.addDoubleInfo(newValue); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return Double.toString(getValue()); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.constValueIndex(getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitDoubleMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java new file mode 100644 index 0000000..fca4034 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java @@ -0,0 +1,126 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import java.io.IOException; +import java.lang.reflect.Method; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; + +/** + * Enum constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class EnumMemberValue extends MemberValue { + int typeIndex, valueIndex; + + /** + * Constructs an enum constant value. The initial value is specified + * by the constant pool entries at the given indexes. + * + * @param type the index of a CONSTANT_Utf8_info structure + * representing the enum type. + * @param value the index of a CONSTANT_Utf8_info structure. + * representing the enum value. + */ + public EnumMemberValue(int type, int value, ConstPool cp) { + super('e', cp); + this.typeIndex = type; + this.valueIndex = value; + } + + /** + * Constructs an enum constant value. + * The initial value is not specified. + */ + public EnumMemberValue(ConstPool cp) { + super('e', cp); + typeIndex = valueIndex = 0; + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) + throws ClassNotFoundException + { + try { + return getType(cl).getField(getValue()).get(null); + } + catch (NoSuchFieldException e) { + throw new ClassNotFoundException(getType() + "." + getValue()); + } + catch (IllegalAccessException e) { + throw new ClassNotFoundException(getType() + "." + getValue()); + } + } + + Class getType(ClassLoader cl) throws ClassNotFoundException { + return loadClass(cl, getType()); + } + + /** + * Obtains the enum type name. + * + * @return a fully-qualified type name. + */ + public String getType() { + return Descriptor.toClassName(cp.getUtf8Info(typeIndex)); + } + + /** + * Changes the enum type name. + * + * @param typename a fully-qualified type name. + */ + public void setType(String typename) { + typeIndex = cp.addUtf8Info(Descriptor.of(typename)); + } + + /** + * Obtains the name of the enum constant value. + */ + public String getValue() { + return cp.getUtf8Info(valueIndex); + } + + /** + * Changes the name of the enum constant value. + */ + public void setValue(String name) { + valueIndex = cp.addUtf8Info(name); + } + + public String toString() { + return getType() + "." + getValue(); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.enumConstValue(cp.getUtf8Info(typeIndex), getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitEnumMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java new file mode 100644 index 0000000..abb5095 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java @@ -0,0 +1,105 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Floating-point number constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + * @version $Revision: 1.7 $ + */ +public class FloatMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs a float constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Float_info structure. + */ + public FloatMemberValue(int index, ConstPool cp) { + super('F', cp); + this.valueIndex = index; + } + + /** + * Constructs a float constant value. + * + * @param f the initial value. + */ + public FloatMemberValue(float f, ConstPool cp) { + super('F', cp); + setValue(f); + } + + /** + * Constructs a float constant value. The initial value is 0.0. + */ + public FloatMemberValue(ConstPool cp) { + super('F', cp); + setValue(0.0F); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) { + return new Float(getValue()); + } + + Class getType(ClassLoader cl) { + return float.class; + } + + /** + * Obtains the value of the member. + */ + public float getValue() { + return cp.getFloatInfo(valueIndex); + } + + /** + * Sets the value of the member. + */ + public void setValue(float newValue) { + valueIndex = cp.addFloatInfo(newValue); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return Float.toString(getValue()); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.constValueIndex(getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitFloatMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java new file mode 100644 index 0000000..a112e4c --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java @@ -0,0 +1,110 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Integer constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class IntegerMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs an int constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Integer_info structure. + */ + public IntegerMemberValue(int index, ConstPool cp) { + super('I', cp); + this.valueIndex = index; + } + + /** + * Constructs an int constant value. + * Note that this constructor receives the initial value + * as the second parameter + * unlike the corresponding constructors in the sibling classes. + * This is for making a difference from the constructor that receives + * an index into the constant pool table as the first parameter. + * Note that the index is also int type. + * + * @param value the initial value. + */ + public IntegerMemberValue(ConstPool cp, int value) { + super('I', cp); + setValue(value); + } + + /** + * Constructs an int constant value. The initial value is 0. + */ + public IntegerMemberValue(ConstPool cp) { + super('I', cp); + setValue(0); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) { + return new Integer(getValue()); + } + + Class getType(ClassLoader cl) { + return int.class; + } + + /** + * Obtains the value of the member. + */ + public int getValue() { + return cp.getIntegerInfo(valueIndex); + } + + /** + * Sets the value of the member. + */ + public void setValue(int newValue) { + valueIndex = cp.addIntegerInfo(newValue); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return Integer.toString(getValue()); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.constValueIndex(getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitIntegerMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java new file mode 100644 index 0000000..9be1876 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java @@ -0,0 +1,104 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Long integer constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class LongMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs a long constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Long_info structure. + */ + public LongMemberValue(int index, ConstPool cp) { + super('J', cp); + this.valueIndex = index; + } + + /** + * Constructs a long constant value. + * + * @param j the initial value. + */ + public LongMemberValue(long j, ConstPool cp) { + super('J', cp); + setValue(j); + } + + /** + * Constructs a long constant value. The initial value is 0. + */ + public LongMemberValue(ConstPool cp) { + super('J', cp); + setValue(0L); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) { + return new Long(getValue()); + } + + Class getType(ClassLoader cl) { + return long.class; + } + + /** + * Obtains the value of the member. + */ + public long getValue() { + return cp.getLongInfo(valueIndex); + } + + /** + * Sets the value of the member. + */ + public void setValue(long newValue) { + valueIndex = cp.addLongInfo(newValue); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return Long.toString(getValue()); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.constValueIndex(getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitLongMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java new file mode 100644 index 0000000..fcbd86f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java @@ -0,0 +1,89 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * The value of a member declared in an annotation. + * + * @see Annotation#getMemberValue(String) + * @author Bill Burke + * @author Shigeru Chiba + */ +public abstract class MemberValue { + ConstPool cp; + char tag; + + MemberValue(char tag, ConstPool cp) { + this.cp = cp; + this.tag = tag; + } + + /** + * Returns the value. If the value type is a primitive type, the + * returned value is boxed. + */ + abstract Object getValue(ClassLoader cl, ClassPool cp, Method m) + throws ClassNotFoundException; + + abstract Class getType(ClassLoader cl) throws ClassNotFoundException; + + static Class loadClass(ClassLoader cl, String classname) + throws ClassNotFoundException, NoSuchClassError + { + try { + return Class.forName(convertFromArray(classname), true, cl); + } + catch (LinkageError e) { + throw new NoSuchClassError(classname, e); + } + } + + private static String convertFromArray(String classname) + { + int index = classname.indexOf("[]"); + if (index != -1) { + String rawType = classname.substring(0, index); + StringBuffer sb = new StringBuffer(Descriptor.of(rawType)); + while (index != -1) { + sb.insert(0, "["); + index = classname.indexOf("[]", index + 1); + } + return sb.toString().replace('/', '.'); + } + return classname; + } + + /** + * Accepts a visitor. + */ + public abstract void accept(MemberValueVisitor visitor); + + /** + * Writes the value. + */ + public abstract void write(AnnotationsWriter w) throws IOException; +} + + diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java new file mode 100644 index 0000000..e20d31e --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java @@ -0,0 +1,39 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +/** + * Visitor for traversing member values included in an annotation. + * + * @see MemberValue#accept(MemberValueVisitor) + * @author Bill Burke + */ +public interface MemberValueVisitor { + public void visitAnnotationMemberValue(AnnotationMemberValue node); + public void visitArrayMemberValue(ArrayMemberValue node); + public void visitBooleanMemberValue(BooleanMemberValue node); + public void visitByteMemberValue(ByteMemberValue node); + public void visitCharMemberValue(CharMemberValue node); + public void visitDoubleMemberValue(DoubleMemberValue node); + public void visitEnumMemberValue(EnumMemberValue node); + public void visitFloatMemberValue(FloatMemberValue node); + public void visitIntegerMemberValue(IntegerMemberValue node); + public void visitLongMemberValue(LongMemberValue node); + public void visitShortMemberValue(ShortMemberValue node); + public void visitStringMemberValue(StringMemberValue node); + public void visitClassMemberValue(ClassMemberValue node); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java new file mode 100644 index 0000000..9b99786 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java @@ -0,0 +1,40 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +/** + * Thrown if the linkage fails. + * It keeps the name of the class that caused this error. + */ +public class NoSuchClassError extends Error { + private String className; + + /** + * Constructs an exception. + */ + public NoSuchClassError(String className, Error cause) { + super(cause.toString(), cause); + this.className = className; + } + + /** + * Returns the name of the class not found. + */ + public String getClassName() { + return className; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java new file mode 100644 index 0000000..8dd4970 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java @@ -0,0 +1,104 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Short integer constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class ShortMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs a short constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Integer_info structure. + */ + public ShortMemberValue(int index, ConstPool cp) { + super('S', cp); + this.valueIndex = index; + } + + /** + * Constructs a short constant value. + * + * @param s the initial value. + */ + public ShortMemberValue(short s, ConstPool cp) { + super('S', cp); + setValue(s); + } + + /** + * Constructs a short constant value. The initial value is 0. + */ + public ShortMemberValue(ConstPool cp) { + super('S', cp); + setValue((short)0); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) { + return new Short(getValue()); + } + + Class getType(ClassLoader cl) { + return short.class; + } + + /** + * Obtains the value of the member. + */ + public short getValue() { + return (short)cp.getIntegerInfo(valueIndex); + } + + /** + * Sets the value of the member. + */ + public void setValue(short newValue) { + valueIndex = cp.addIntegerInfo(newValue); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return Short.toString(getValue()); + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.constValueIndex(getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitShortMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java new file mode 100644 index 0000000..61ac276 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java @@ -0,0 +1,104 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 2004 Bill Burke. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.annotation; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * String constant value. + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class StringMemberValue extends MemberValue { + int valueIndex; + + /** + * Constructs a string constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Utf8_info structure. + */ + public StringMemberValue(int index, ConstPool cp) { + super('s', cp); + this.valueIndex = index; + } + + /** + * Constructs a string constant value. + * + * @param str the initial value. + */ + public StringMemberValue(String str, ConstPool cp) { + super('s', cp); + setValue(str); + } + + /** + * Constructs a string constant value. The initial value is "". + */ + public StringMemberValue(ConstPool cp) { + super('s', cp); + setValue(""); + } + + Object getValue(ClassLoader cl, ClassPool cp, Method m) { + return getValue(); + } + + Class getType(ClassLoader cl) { + return String.class; + } + + /** + * Obtains the value of the member. + */ + public String getValue() { + return cp.getUtf8Info(valueIndex); + } + + /** + * Sets the value of the member. + */ + public void setValue(String newValue) { + valueIndex = cp.addUtf8Info(newValue); + } + + /** + * Obtains the string representation of this object. + */ + public String toString() { + return "\"" + getValue() + "\""; + } + + /** + * Writes the value. + */ + public void write(AnnotationsWriter writer) throws IOException { + writer.constValueIndex(getValue()); + } + + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitStringMemberValue(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java new file mode 100644 index 0000000..c35185e --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java @@ -0,0 +1,174 @@ +package com.wenshuo.agent.javassist.bytecode.annotation; + +import java.io.IOException; +import java.io.OutputStream; + +import com.wenshuo.agent.javassist.bytecode.ConstPool; + +/** + * A convenience class for constructing a + * {@code ..TypeAnnotations_attribute}. + * See the source code of the {@link javassist.bytecode.TypeAnnotationsAttribute} class. + * + * @since 3.19 + */ +public class TypeAnnotationsWriter extends AnnotationsWriter { + /** + * Constructs with the given output stream. + * + * @param os the output stream. + * @param cp the constant pool. + */ + public TypeAnnotationsWriter(OutputStream os, ConstPool cp) { + super(os, cp); + } + + /** + * Writes {@code num_annotations} in + * {@code Runtime(In)VisibleTypeAnnotations_attribute}. + * It must be followed by {@code num} instances of {@code type_annotation}. + */ + public void numAnnotations(int num) throws IOException { + super.numAnnotations(num); + } + + /** + * Writes {@code target_type} and {@code type_parameter_target} + * of {@code target_info} union. + */ + public void typeParameterTarget(int targetType, int typeParameterIndex) + throws IOException + { + output.write(targetType); + output.write(typeParameterIndex); + } + + /** + * Writes {@code target_type} and {@code supertype_target} + * of {@code target_info} union. + */ + public void supertypeTarget(int supertypeIndex) + throws IOException + { + output.write(0x10); + write16bit(supertypeIndex); + } + + /** + * Writes {@code target_type} and {@code type_parameter_bound_target} + * of {@code target_info} union. + */ + public void typeParameterBoundTarget(int targetType, int typeParameterIndex, int boundIndex) + throws IOException + { + output.write(targetType); + output.write(typeParameterIndex); + output.write(boundIndex); + } + + /** + * Writes {@code target_type} and {@code empty_target} + * of {@code target_info} union. + */ + public void emptyTarget(int targetType) throws IOException { + output.write(targetType); + } + + /** + * Writes {@code target_type} and {@code type_parameter_target} + * of {@code target_info} union. + */ + public void formalParameterTarget(int formalParameterIndex) + throws IOException + { + output.write(0x16); + output.write(formalParameterIndex); + } + + /** + * Writes {@code target_type} and {@code throws_target} + * of {@code target_info} union. + */ + public void throwsTarget(int throwsTypeIndex) + throws IOException + { + output.write(0x17); + write16bit(throwsTypeIndex); + } + + /** + * Writes {@code target_type} and {@code localvar_target} + * of {@code target_info} union. + * It must be followed by {@code tableLength} calls + * to {@code localVarTargetTable}. + */ + public void localVarTarget(int targetType, int tableLength) + throws IOException + { + output.write(targetType); + write16bit(tableLength); + } + + /** + * Writes an element of {@code table[]} of {@code localvar_target} + * of {@code target_info} union. + */ + public void localVarTargetTable(int startPc, int length, int index) + throws IOException + { + write16bit(startPc); + write16bit(length); + write16bit(index); + } + + /** + * Writes {@code target_type} and {@code catch_target} + * of {@code target_info} union. + */ + public void catchTarget(int exceptionTableIndex) + throws IOException + { + output.write(0x42); + write16bit(exceptionTableIndex); + } + + /** + * Writes {@code target_type} and {@code offset_target} + * of {@code target_info} union. + */ + public void offsetTarget(int targetType, int offset) + throws IOException + { + output.write(targetType); + write16bit(offset); + } + + /** + * Writes {@code target_type} and {@code type_argument_target} + * of {@code target_info} union. + */ + public void typeArgumentTarget(int targetType, int offset, int type_argument_index) + throws IOException + { + output.write(targetType); + write16bit(offset); + output.write(type_argument_index); + } + + /** + * Writes {@code path_length} of {@code type_path}. + */ + public void typePath(int pathLength) throws IOException { + output.write(pathLength); + } + + /** + * Writes an element of {@code path[]} of {@code type_path}. + */ + public void typePathPath(int typePathKind, int typeArgumentIndex) + throws IOException + { + output.write(typePathKind); + output.write(typeArgumentIndex); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/package.html b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/package.html new file mode 100644 index 0000000..d0656db --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/package.html @@ -0,0 +1,8 @@ + + +Bytecode-level Annotations API. + +

This package provides low-level API for editing annotations attributes. + + + diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/package.html b/src/main/java/com/wenshuo/agent/javassist/bytecode/package.html new file mode 100644 index 0000000..9da3888 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/package.html @@ -0,0 +1,18 @@ + + +Bytecode-level API. + +

This package provides low-level API for editing a raw class file. +It allows the users to read and modify a constant pool entry, a single +bytecode instruction, and so on. + +

The users of this package must know the specifications of +class file and Java bytecode. For more details, read this book: + +

    Tim Lindholm and Frank Yellin, +"The Java Virtual Machine Specification 2nd Ed.", +Addison-Wesley, 1999. +
+ + + diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java new file mode 100644 index 0000000..f85821b --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java @@ -0,0 +1,411 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.stackmap; + +import com.wenshuo.agent.javassist.bytecode.*; +import java.util.HashMap; +import java.util.ArrayList; + +/** + * A basic block is a sequence of bytecode that does not contain jump/branch + * instructions except at the last bytecode. + * Since Java7 or later does not allow JSR, this class throws an exception when + * it finds JSR. + */ +public class BasicBlock { + static class JsrBytecode extends BadBytecode { + JsrBytecode() { super("JSR"); } + } + + protected int position, length; + protected int incoming; // the number of incoming branches. + protected BasicBlock[] exit; // null if the block is a leaf. + protected boolean stop; // true if the block ends with an unconditional jump. + protected Catch toCatch; + + protected BasicBlock(int pos) { + position = pos; + length = 0; + incoming = 0; + } + + public static BasicBlock find(BasicBlock[] blocks, int pos) + throws BadBytecode + { + for (int i = 0; i < blocks.length; i++) { + int iPos = blocks[i].position; + if (iPos <= pos && pos < iPos + blocks[i].length) + return blocks[i]; + } + + throw new BadBytecode("no basic block at " + pos); + } + + public static class Catch { + public Catch next; + public BasicBlock body; + public int typeIndex; + Catch(BasicBlock b, int i, Catch c) { + body = b; + typeIndex = i; + next = c; + } + } + + public String toString() { + StringBuffer sbuf = new StringBuffer(); + String cname = this.getClass().getName(); + int i = cname.lastIndexOf('.'); + sbuf.append(i < 0 ? cname : cname.substring(i + 1)); + sbuf.append("["); + toString2(sbuf); + sbuf.append("]"); + return sbuf.toString(); + } + + protected void toString2(StringBuffer sbuf) { + sbuf.append("pos=").append(position).append(", len=") + .append(length).append(", in=").append(incoming) + .append(", exit{"); + if (exit != null) { + for (int i = 0; i < exit.length; i++) + sbuf.append(exit[i].position).append(","); + } + + sbuf.append("}, {"); + Catch th = toCatch; + while (th != null) { + sbuf.append("(").append(th.body.position).append(", ") + .append(th.typeIndex).append("), "); + th = th.next; + } + + sbuf.append("}"); + } + + /** + * A Mark indicates the position of a branch instruction + * or a branch target. + */ + static class Mark implements Comparable { + int position; + BasicBlock block; + BasicBlock[] jump; + boolean alwaysJmp; // true if an unconditional branch. + int size; // 0 unless the mark indicates RETURN etc. + Catch catcher; + + Mark(int p) { + position = p; + block = null; + jump = null; + alwaysJmp = false; + size = 0; + catcher = null; + } + + public int compareTo(Object obj) { + if (obj instanceof Mark) { + int pos = ((Mark)obj).position; + return position - pos; + } + + return -1; + } + + void setJump(BasicBlock[] bb, int s, boolean always) { + jump = bb; + size = s; + alwaysJmp = always; + } + } + + public static class Maker { + /* Override these two methods if a subclass of BasicBlock must be + * instantiated. + */ + protected BasicBlock makeBlock(int pos) { + return new BasicBlock(pos); + } + + protected BasicBlock[] makeArray(int size) { + return new BasicBlock[size]; + } + + private BasicBlock[] makeArray(BasicBlock b) { + BasicBlock[] array = makeArray(1); + array[0] = b; + return array; + } + + private BasicBlock[] makeArray(BasicBlock b1, BasicBlock b2) { + BasicBlock[] array = makeArray(2); + array[0] = b1; + array[1] = b2; + return array; + } + + public BasicBlock[] make(MethodInfo minfo) throws BadBytecode { + CodeAttribute ca = minfo.getCodeAttribute(); + if (ca == null) + return null; + + CodeIterator ci = ca.iterator(); + return make(ci, 0, ci.getCodeLength(), ca.getExceptionTable()); + } + + public BasicBlock[] make(CodeIterator ci, int begin, int end, + ExceptionTable et) + throws BadBytecode + { + HashMap marks = makeMarks(ci, begin, end, et); + BasicBlock[] bb = makeBlocks(marks); + addCatchers(bb, et); + return bb; + } + + /* Branch target + */ + private Mark makeMark(HashMap table, int pos) { + return makeMark0(table, pos, true, true); + } + + /* Branch instruction. + * size > 0 + */ + private Mark makeMark(HashMap table, int pos, BasicBlock[] jump, + int size, boolean always) { + Mark m = makeMark0(table, pos, false, false); + m.setJump(jump, size, always); + return m; + } + + private Mark makeMark0(HashMap table, int pos, + boolean isBlockBegin, boolean isTarget) { + Integer p = new Integer(pos); + Mark m = (Mark)table.get(p); + if (m == null) { + m = new Mark(pos); + table.put(p, m); + } + + if (isBlockBegin) { + if (m.block == null) + m.block = makeBlock(pos); + + if (isTarget) + m.block.incoming++; + } + + return m; + } + + private HashMap makeMarks(CodeIterator ci, int begin, int end, + ExceptionTable et) + throws BadBytecode + { + ci.begin(); + ci.move(begin); + HashMap marks = new HashMap(); + while (ci.hasNext()) { + int index = ci.next(); + if (index >= end) + break; + + int op = ci.byteAt(index); + if ((Opcode.IFEQ <= op && op <= Opcode.IF_ACMPNE) + || op == Opcode.IFNULL || op == Opcode.IFNONNULL) { + Mark to = makeMark(marks, index + ci.s16bitAt(index + 1)); + Mark next = makeMark(marks, index + 3); + makeMark(marks, index, makeArray(to.block, next.block), 3, false); + } + else if (Opcode.GOTO <= op && op <= Opcode.LOOKUPSWITCH) + switch (op) { + case Opcode.GOTO : + makeGoto(marks, index, index + ci.s16bitAt(index + 1), 3); + break; + case Opcode.JSR : + makeJsr(marks, index, index + ci.s16bitAt(index + 1), 3); + break; + case Opcode.RET : + makeMark(marks, index, null, 2, true); + break; + case Opcode.TABLESWITCH : { + int pos = (index & ~3) + 4; + int low = ci.s32bitAt(pos + 4); + int high = ci.s32bitAt(pos + 8); + int ncases = high - low + 1; + BasicBlock[] to = makeArray(ncases + 1); + to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target + int p = pos + 12; + int n = p + ncases * 4; + int k = 1; + while (p < n) { + to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block; + p += 4; + } + makeMark(marks, index, to, n - index, true); + break; } + case Opcode.LOOKUPSWITCH : { + int pos = (index & ~3) + 4; + int ncases = ci.s32bitAt(pos + 4); + BasicBlock[] to = makeArray(ncases + 1); + to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target + int p = pos + 8 + 4; + int n = p + ncases * 8 - 4; + int k = 1; + while (p < n) { + to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block; + p += 8; + } + makeMark(marks, index, to, n - index, true); + break; } + } + else if ((Opcode.IRETURN <= op && op <= Opcode.RETURN) || op == Opcode.ATHROW) + makeMark(marks, index, null, 1, true); + else if (op == Opcode.GOTO_W) + makeGoto(marks, index, index + ci.s32bitAt(index + 1), 5); + else if (op == Opcode.JSR_W) + makeJsr(marks, index, index + ci.s32bitAt(index + 1), 5); + else if (op == Opcode.WIDE && ci.byteAt(index + 1) == Opcode.RET) + makeMark(marks, index, null, 4, true); + } + + if (et != null) { + int i = et.size(); + while (--i >= 0) { + makeMark0(marks, et.startPc(i), true, false); + makeMark(marks, et.handlerPc(i)); + } + } + + return marks; + } + + private void makeGoto(HashMap marks, int pos, int target, int size) { + Mark to = makeMark(marks, target); + BasicBlock[] jumps = makeArray(to.block); + makeMark(marks, pos, jumps, size, true); + } + + /* + * We could ignore JSR since Java 7 or later does not allow it. + * See The JVM Spec. Sec. 4.10.2.5. + */ + protected void makeJsr(HashMap marks, int pos, int target, int size) throws BadBytecode { + /* + Mark to = makeMark(marks, target); + Mark next = makeMark(marks, pos + size); + BasicBlock[] jumps = makeArray(to.block, next.block); + makeMark(marks, pos, jumps, size, false); + */ + throw new JsrBytecode(); + } + + private BasicBlock[] makeBlocks(HashMap markTable) { + Mark[] marks = (Mark[])markTable.values() + .toArray(new Mark[markTable.size()]); + java.util.Arrays.sort(marks); + ArrayList blocks = new ArrayList(); + int i = 0; + BasicBlock prev; + if (marks.length > 0 && marks[0].position == 0 && marks[0].block != null) + prev = getBBlock(marks[i++]); + else + prev = makeBlock(0); + + blocks.add(prev); + while (i < marks.length) { + Mark m = marks[i++]; + BasicBlock bb = getBBlock(m); + if (bb == null) { + // the mark indicates a branch instruction + if (prev.length > 0) { + // the previous mark already has exits. + prev = makeBlock(prev.position + prev.length); + blocks.add(prev); + } + + prev.length = m.position + m.size - prev.position; + prev.exit = m.jump; + prev.stop = m.alwaysJmp; + } + else { + // the mark indicates a branch target + if (prev.length == 0) { + prev.length = m.position - prev.position; + bb.incoming++; + prev.exit = makeArray(bb); + } + else { + // the previous mark already has exits. + if (prev.position + prev.length < m.position) { + // dead code is found. + prev = makeBlock(prev.position + prev.length); + blocks.add(prev); + prev.length = m.position - prev.position; + // the incoming flow from dead code is not counted + // bb.incoming++; + prev.stop = true; // because the incoming flow is not counted. + prev.exit = makeArray(bb); + } + } + + blocks.add(bb); + prev = bb; + } + } + + return (BasicBlock[])blocks.toArray(makeArray(blocks.size())); + } + + private static BasicBlock getBBlock(Mark m) { + BasicBlock b = m.block; + if (b != null && m.size > 0) { + b.exit = m.jump; + b.length = m.size; + b.stop = m.alwaysJmp; + } + + return b; + } + + private void addCatchers(BasicBlock[] blocks, ExceptionTable et) + throws BadBytecode + { + if (et == null) + return; + + int i = et.size(); + while (--i >= 0) { + BasicBlock handler = find(blocks, et.handlerPc(i)); + int start = et.startPc(i); + int end = et.endPc(i); + int type = et.catchType(i); + handler.incoming--; + for (int k = 0; k < blocks.length; k++) { + BasicBlock bb = blocks[k]; + int iPos = bb.position; + if (start <= iPos && iPos < end) { + bb.toCatch = new Catch(handler, type, bb.toCatch); + handler.incoming++; + } + } + } + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java new file mode 100644 index 0000000..19f64d5 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java @@ -0,0 +1,606 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.stackmap; + +import java.util.ArrayList; +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.*; + +/** + * Stack map maker. + */ +public class MapMaker extends Tracer { + /* + public static void main(String[] args) throws Exception { + boolean useMain2 = args[0].equals("0"); + if (useMain2 && args.length > 1) { + main2(args); + return; + } + + for (int i = 0; i < args.length; i++) + main1(args[i]); + } + + public static void main1(String className) throws Exception { + ClassPool cp = ClassPool.getDefault(); + //javassist.CtClass cc = cp.get(className); + javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(className)); + System.out.println(className); + ClassFile cf = cc.getClassFile(); + java.util.List minfos = cf.getMethods(); + for (int i = 0; i < minfos.size(); i++) { + MethodInfo minfo = (MethodInfo)minfos.get(i); + CodeAttribute ca = minfo.getCodeAttribute(); + if (ca != null) + ca.setAttribute(make(cp, minfo)); + } + + cc.writeFile("tmp"); + } + + public static void main2(String[] args) throws Exception { + ClassPool cp = ClassPool.getDefault(); + //javassist.CtClass cc = cp.get(args[1]); + javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(args[1])); + MethodInfo minfo; + if (args[2].equals("_init_")) + minfo = cc.getDeclaredConstructors()[0].getMethodInfo(); + // minfo = cc.getClassInitializer().getMethodInfo(); + else + minfo = cc.getDeclaredMethod(args[2]).getMethodInfo(); + + CodeAttribute ca = minfo.getCodeAttribute(); + if (ca == null) { + System.out.println("abstarct method"); + return; + } + + TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, false); + MapMaker mm = new MapMaker(cp, minfo, ca); + mm.make(blocks, ca.getCode()); + for (int i = 0; i < blocks.length; i++) + System.out.println(blocks[i]); + } + */ + + /** + * Computes the stack map table of the given method and returns it. + * It returns null if the given method does not have to have a + * stack map table or it includes JSR. + */ + public static StackMapTable make(ClassPool classes, MethodInfo minfo) + throws BadBytecode + { + CodeAttribute ca = minfo.getCodeAttribute(); + if (ca == null) + return null; + + TypedBlock[] blocks; + try { + blocks = TypedBlock.makeBlocks(minfo, ca, true); + } + catch (BasicBlock.JsrBytecode e) { + return null; + } + + if (blocks == null) + return null; + + MapMaker mm = new MapMaker(classes, minfo, ca); + try { + mm.make(blocks, ca.getCode()); + } + catch (BadBytecode bb) { + throw new BadBytecode(minfo, bb); + } + + return mm.toStackMap(blocks); + } + + /** + * Computes the stack map table for J2ME. + * It returns null if the given method does not have to have a + * stack map table or it includes JSR. + */ + public static StackMap make2(ClassPool classes, MethodInfo minfo) + throws BadBytecode + { + CodeAttribute ca = minfo.getCodeAttribute(); + if (ca == null) + return null; + + TypedBlock[] blocks; + try { + blocks = TypedBlock.makeBlocks(minfo, ca, true); + } + catch (BasicBlock.JsrBytecode e) { + return null; + } + + if (blocks == null) + return null; + + MapMaker mm = new MapMaker(classes, minfo, ca); + try { + mm.make(blocks, ca.getCode()); + } + catch (BadBytecode bb) { + throw new BadBytecode(minfo, bb); + } + return mm.toStackMap2(minfo.getConstPool(), blocks); + } + + public MapMaker(ClassPool classes, MethodInfo minfo, CodeAttribute ca) { + super(classes, minfo.getConstPool(), + ca.getMaxStack(), ca.getMaxLocals(), + TypedBlock.getRetType(minfo.getDescriptor())); + } + + protected MapMaker(MapMaker old) { super(old); } + + /** + * Runs an analyzer (Phase 1 and 2). + */ + void make(TypedBlock[] blocks, byte[] code) + throws BadBytecode + { + make(code, blocks[0]); + findDeadCatchers(code, blocks); + try { + fixTypes(code, blocks); + } catch (NotFoundException e) { + throw new BadBytecode("failed to resolve types", e); + } + } + + // Phase 1 + + private void make(byte[] code, TypedBlock tb) + throws BadBytecode + { + copyTypeData(tb.stackTop, tb.stackTypes, stackTypes); + stackTop = tb.stackTop; + copyTypeData(tb.localsTypes.length, tb.localsTypes, localsTypes); + + traceException(code, tb.toCatch); + + int pos = tb.position; + int end = pos + tb.length; + while (pos < end) { + pos += doOpcode(pos, code); + traceException(code, tb.toCatch); + } + + if (tb.exit != null) { + for (int i = 0; i < tb.exit.length; i++) { + TypedBlock e = (TypedBlock)tb.exit[i]; + if (e.alreadySet()) + mergeMap(e, true); + else { + recordStackMap(e); + MapMaker maker = new MapMaker(this); + maker.make(code, e); + } + } + } + } + + private void traceException(byte[] code, TypedBlock.Catch handler) + throws BadBytecode + { + while (handler != null) { + TypedBlock tb = (TypedBlock)handler.body; + if (tb.alreadySet()) { + mergeMap(tb, false); + if (tb.stackTop < 1) + throw new BadBytecode("bad catch clause: " + handler.typeIndex); + + tb.stackTypes[0] = merge(toExceptionType(handler.typeIndex), + tb.stackTypes[0]); + } + else { + recordStackMap(tb, handler.typeIndex); + MapMaker maker = new MapMaker(this); + maker.make(code, tb); + } + + handler = handler.next; + } + } + + private void mergeMap(TypedBlock dest, boolean mergeStack) throws BadBytecode { + int n = localsTypes.length; + for (int i = 0; i < n; i++) + dest.localsTypes[i] = merge(validateTypeData(localsTypes, n, i), + dest.localsTypes[i]); + + if (mergeStack) { + n = stackTop; + for (int i = 0; i < n; i++) + dest.stackTypes[i] = merge(stackTypes[i], dest.stackTypes[i]); + } + } + + private TypeData merge(TypeData src, TypeData target) throws BadBytecode { + if (src == target) + return target; + else if (target instanceof TypeData.ClassName + || target instanceof TypeData.BasicType) // a parameter + return target; + else if (target instanceof TypeData.AbsTypeVar) { + ((TypeData.AbsTypeVar)target).merge(src); + return target; + } + else + throw new RuntimeException("fatal: this should never happen"); + } + + private void recordStackMap(TypedBlock target) + throws BadBytecode + { + TypeData[] tStackTypes = TypeData.make(stackTypes.length); + int st = stackTop; + recordTypeData(st, stackTypes, tStackTypes); + recordStackMap0(target, st, tStackTypes); + } + + private void recordStackMap(TypedBlock target, int exceptionType) + throws BadBytecode + { + TypeData[] tStackTypes = TypeData.make(stackTypes.length); + tStackTypes[0] = toExceptionType(exceptionType).join(); + recordStackMap0(target, 1, tStackTypes); + } + + private TypeData.ClassName toExceptionType(int exceptionType) { + String type; + if (exceptionType == 0) // for finally clauses + type= "java.lang.Throwable"; + else + type = cpool.getClassInfo(exceptionType); + + return new TypeData.ClassName(type); + } + + private void recordStackMap0(TypedBlock target, int st, TypeData[] tStackTypes) + throws BadBytecode + { + int n = localsTypes.length; + TypeData[] tLocalsTypes = TypeData.make(n); + int k = recordTypeData(n, localsTypes, tLocalsTypes); + target.setStackMap(st, tStackTypes, k, tLocalsTypes); + } + + protected static int recordTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) { + int k = -1; + for (int i = 0; i < n; i++) { + TypeData t = validateTypeData(srcTypes, n, i); + destTypes[i] = t.join(); + if (t != TOP) + k = i + 1; // t might be long or double. + } + + return k + 1; + } + + protected static void copyTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) { + for (int i = 0; i < n; i++) + destTypes[i] = srcTypes[i]; + } + + private static TypeData validateTypeData(TypeData[] data, int length, int index) { + TypeData td = data[index]; + if (td.is2WordType() && index + 1 < length) + if (data[index + 1] != TOP) + return TOP; + + return td; + } + + // Phase 1.5 + + /* + * Javac may generate an exception handler that catches only the exception + * thrown within the handler itself. It is dead code. + * See javassist.JvstTest4.testJIRA195(). + */ + + private void findDeadCatchers(byte[] code, TypedBlock[] blocks) throws BadBytecode { + int len = blocks.length; + for (int i = 0; i < len; i++) { + TypedBlock block = blocks[i]; + if (!block.alreadySet()) { + fixDeadcode(code, block); + BasicBlock.Catch handler = block.toCatch; + if (handler != null) { + TypedBlock tb = (TypedBlock)handler.body; + if (!tb.alreadySet()) { + // tb is a handler that catches only the exceptions + // thrown from dead code. + recordStackMap(tb, handler.typeIndex); + fixDeadcode(code, tb); + tb.incoming = 1; + } + } + + } + } + } + + private void fixDeadcode(byte[] code, TypedBlock block) throws BadBytecode { + int pos = block.position; + int len = block.length - 3; + if (len < 0) { + // if the dead-code length is shorter than 3 bytes. + if (len == -1) + code[pos] = Bytecode.NOP; + + code[pos + block.length - 1] = (byte)Bytecode.ATHROW; + block.incoming = 1; + recordStackMap(block, 0); + return; + } + + // if block.incomping > 0, all the incoming edges are from + // other dead code blocks. So set block.incoming to 0. + block.incoming = 0; + + for (int k = 0; k < len; k++) + code[pos + k] = Bytecode.NOP; + + code[pos + len] = (byte)Bytecode.GOTO; + ByteArray.write16bit(-len, code, pos + len + 1); + } + + // Phase 2 + + /* + * This method first finds strongly connected components (SCCs) + * in a TypeData graph by Tarjan's algorithm. + * SCCs are TypeData nodes sharing the same type. + * Since SCCs are found in the topologically sorted order, + * their types are also fixed when they are found. + */ + private void fixTypes(byte[] code, TypedBlock[] blocks) throws NotFoundException, BadBytecode { + ArrayList preOrder = new ArrayList(); + int len = blocks.length; + int index = 0; + for (int i = 0; i < len; i++) { + TypedBlock block = blocks[i]; + if (block.alreadySet()) { // if block is not dead code + int n = block.localsTypes.length; + for (int j = 0; j < n; j++) + index = block.localsTypes[j].dfs(preOrder, index, classPool); + + n = block.stackTop; + for (int j = 0; j < n; j++) + index = block.stackTypes[j].dfs(preOrder, index, classPool); + } + } + } + + // Phase 3 + + public StackMapTable toStackMap(TypedBlock[] blocks) { + StackMapTable.Writer writer = new StackMapTable.Writer(32); + int n = blocks.length; + TypedBlock prev = blocks[0]; + int offsetDelta = prev.length; + if (prev.incoming > 0) { // the first instruction is a branch target. + writer.sameFrame(0); + offsetDelta--; + } + + for (int i = 1; i < n; i++) { + TypedBlock bb = blocks[i]; + if (isTarget(bb, blocks[i - 1])) { + bb.resetNumLocals(); + int diffL = stackMapDiff(prev.numLocals, prev.localsTypes, + bb.numLocals, bb.localsTypes); + toStackMapBody(writer, bb, diffL, offsetDelta, prev); + offsetDelta = bb.length - 1; + prev = bb; + } + else if (bb.incoming == 0) { + // dead code. + writer.sameFrame(offsetDelta); + offsetDelta = bb.length - 1; + prev = bb; + } + else + offsetDelta += bb.length; + } + + return writer.toStackMapTable(cpool); + } + + /** + * Returns true if cur is a branch target. + */ + private boolean isTarget(TypedBlock cur, TypedBlock prev) { + int in = cur.incoming; + if (in > 1) + return true; + else if (in < 1) + return false; + + return prev.stop; + } + + private void toStackMapBody(StackMapTable.Writer writer, TypedBlock bb, + int diffL, int offsetDelta, TypedBlock prev) { + // if diffL is -100, two TypeData arrays do not share + // any elements. + + int stackTop = bb.stackTop; + if (stackTop == 0) { + if (diffL == 0) { + writer.sameFrame(offsetDelta); + return; + } + else if (0 > diffL && diffL >= -3) { + writer.chopFrame(offsetDelta, -diffL); + return; + } + else if (0 < diffL && diffL <= 3) { + int[] data = new int[diffL]; + int[] tags = fillStackMap(bb.numLocals - prev.numLocals, + prev.numLocals, data, + bb.localsTypes); + writer.appendFrame(offsetDelta, tags, data); + return; + } + } + else if (stackTop == 1 && diffL == 0) { + TypeData td = bb.stackTypes[0]; + writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool)); + return; + } + else if (stackTop == 2 && diffL == 0) { + TypeData td = bb.stackTypes[0]; + if (td.is2WordType()) { + // bb.stackTypes[1] must be TOP. + writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool)); + return; + } + } + + int[] sdata = new int[stackTop]; + int[] stags = fillStackMap(stackTop, 0, sdata, bb.stackTypes); + int[] ldata = new int[bb.numLocals]; + int[] ltags = fillStackMap(bb.numLocals, 0, ldata, bb.localsTypes); + writer.fullFrame(offsetDelta, ltags, ldata, stags, sdata); + } + + private int[] fillStackMap(int num, int offset, int[] data, TypeData[] types) { + int realNum = diffSize(types, offset, offset + num); + ConstPool cp = cpool; + int[] tags = new int[realNum]; + int j = 0; + for (int i = 0; i < num; i++) { + TypeData td = types[offset + i]; + tags[j] = td.getTypeTag(); + data[j] = td.getTypeData(cp); + if (td.is2WordType()) + i++; + + j++; + } + + return tags; + } + + private static int stackMapDiff(int oldTdLen, TypeData[] oldTd, + int newTdLen, TypeData[] newTd) + { + int diff = newTdLen - oldTdLen; + int len; + if (diff > 0) + len = oldTdLen; + else + len = newTdLen; + + if (stackMapEq(oldTd, newTd, len)) + if (diff > 0) + return diffSize(newTd, len, newTdLen); + else + return -diffSize(oldTd, len, oldTdLen); + else + return -100; + } + + private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) { + for (int i = 0; i < len; i++) { + if (!oldTd[i].eq(newTd[i])) + return false; + } + + return true; + } + + private static int diffSize(TypeData[] types, int offset, int len) { + int num = 0; + while (offset < len) { + TypeData td = types[offset++]; + num++; + if (td.is2WordType()) + offset++; + } + + return num; + } + + // Phase 3 for J2ME + + public StackMap toStackMap2(ConstPool cp, TypedBlock[] blocks) { + StackMap.Writer writer = new StackMap.Writer(); + int n = blocks.length; // should be > 0 + boolean[] effective = new boolean[n]; + TypedBlock prev = blocks[0]; + + // Is the first instruction a branch target? + effective[0] = prev.incoming > 0; + + int num = effective[0] ? 1 : 0; + for (int i = 1; i < n; i++) { + TypedBlock bb = blocks[i]; + if (effective[i] = isTarget(bb, blocks[i - 1])) { + bb.resetNumLocals(); + prev = bb; + num++; + } + } + + if (num == 0) + return null; + + writer.write16bit(num); + for (int i = 0; i < n; i++) + if (effective[i]) + writeStackFrame(writer, cp, blocks[i].position, blocks[i]); + + return writer.toStackMap(cp); + } + + private void writeStackFrame(StackMap.Writer writer, ConstPool cp, int offset, TypedBlock tb) { + writer.write16bit(offset); + writeVerifyTypeInfo(writer, cp, tb.localsTypes, tb.numLocals); + writeVerifyTypeInfo(writer, cp, tb.stackTypes, tb.stackTop); + } + + private void writeVerifyTypeInfo(StackMap.Writer writer, ConstPool cp, TypeData[] types, int num) { + int numDWord = 0; + for (int i = 0; i < num; i++) { + TypeData td = types[i]; + if (td != null && td.is2WordType()) { + numDWord++; + i++; + } + } + + writer.write16bit(num - numDWord); + for (int i = 0; i < num; i++) { + TypeData td = types[i]; + writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp)); + if (td.is2WordType()) + i++; + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java new file mode 100644 index 0000000..caef5c9 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java @@ -0,0 +1,933 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.stackmap; + +import com.wenshuo.agent.javassist.bytecode.ByteArray; +import com.wenshuo.agent.javassist.bytecode.Opcode; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.ClassPool; + +/* + * A class for performing abstract interpretation. + * See also MapMaker class. + */ + +public abstract class Tracer implements TypeTag { + protected ClassPool classPool; + protected ConstPool cpool; + protected String returnType; // used as the type of ARETURN + + protected int stackTop; + protected TypeData[] stackTypes; + protected TypeData[] localsTypes; + + public Tracer(ClassPool classes, ConstPool cp, int maxStack, int maxLocals, + String retType) { + classPool = classes; + cpool = cp; + returnType = retType; + stackTop = 0; + stackTypes = TypeData.make(maxStack); + localsTypes = TypeData.make(maxLocals); + } + + public Tracer(Tracer t) { + classPool = t.classPool; + cpool = t.cpool; + returnType = t.returnType; + stackTop = t.stackTop; + stackTypes = TypeData.make(t.stackTypes.length); + localsTypes = TypeData.make(t.localsTypes.length); + } + + /** + * Does abstract interpretation on the given bytecode instruction. + * It records whether or not a local variable (i.e. register) is accessed. + * If the instruction requires that a local variable or + * a stack element has a more specific type, this method updates the + * type of it. + * + * @param pos the position of the instruction. + * @return the size of the instruction at POS. + */ + protected int doOpcode(int pos, byte[] code) throws BadBytecode { + try { + int op = code[pos] & 0xff; + if (op < 96) + if (op < 54) + return doOpcode0_53(pos, code, op); + else + return doOpcode54_95(pos, code, op); + else + if (op < 148) + return doOpcode96_147(pos, code, op); + else + return doOpcode148_201(pos, code, op); + } + catch (ArrayIndexOutOfBoundsException e) { + throw new BadBytecode("inconsistent stack height " + e.getMessage(), e); + } + } + + protected void visitBranch(int pos, byte[] code, int offset) throws BadBytecode {} + protected void visitGoto(int pos, byte[] code, int offset) throws BadBytecode {} + protected void visitReturn(int pos, byte[] code) throws BadBytecode {} + protected void visitThrow(int pos, byte[] code) throws BadBytecode {} + + /** + * @param pos the position of TABLESWITCH + * @param code bytecode + * @param n the number of case labels + * @param offsetPos the position of the branch-target table. + * @param defaultOffset the offset to the default branch target. + */ + protected void visitTableSwitch(int pos, byte[] code, int n, + int offsetPos, int defaultOffset) throws BadBytecode {} + + /** + * @param pos the position of LOOKUPSWITCH + * @param code bytecode + * @param n the number of case labels + * @param pairsPos the position of the table of pairs of a value and a branch target. + * @param defaultOffset the offset to the default branch target. + */ + protected void visitLookupSwitch(int pos, byte[] code, int n, + int pairsPos, int defaultOffset) throws BadBytecode {} + + /** + * Invoked when the visited instruction is jsr. + * Java6 or later does not allow using RET. + */ + protected void visitJSR(int pos, byte[] code) throws BadBytecode { + /* Since JSR pushes a return address onto the operand stack, + * the stack map at the entry point of a subroutine is + * stackTypes resulting after executing the following code: + * + * stackTypes[stackTop++] = TOP; + */ + } + + /** + * Invoked when the visited instruction is ret or wide ret. + * Java6 or later does not allow using RET. + */ + protected void visitRET(int pos, byte[] code) throws BadBytecode {} + + private int doOpcode0_53(int pos, byte[] code, int op) throws BadBytecode { + int reg; + TypeData[] stackTypes = this.stackTypes; + switch (op) { + case Opcode.NOP : + break; + case Opcode.ACONST_NULL : + stackTypes[stackTop++] = new TypeData.NullType(); + break; + case Opcode.ICONST_M1 : + case Opcode.ICONST_0 : + case Opcode.ICONST_1 : + case Opcode.ICONST_2 : + case Opcode.ICONST_3 : + case Opcode.ICONST_4 : + case Opcode.ICONST_5 : + stackTypes[stackTop++] = INTEGER; + break; + case Opcode.LCONST_0 : + case Opcode.LCONST_1 : + stackTypes[stackTop++] = LONG; + stackTypes[stackTop++] = TOP; + break; + case Opcode.FCONST_0 : + case Opcode.FCONST_1 : + case Opcode.FCONST_2 : + stackTypes[stackTop++] = FLOAT; + break; + case Opcode.DCONST_0 : + case Opcode.DCONST_1 : + stackTypes[stackTop++] = DOUBLE; + stackTypes[stackTop++] = TOP; + break; + case Opcode.BIPUSH : + case Opcode.SIPUSH : + stackTypes[stackTop++] = INTEGER; + return op == Opcode.SIPUSH ? 3 : 2; + case Opcode.LDC : + doLDC(code[pos + 1] & 0xff); + return 2; + case Opcode.LDC_W : + case Opcode.LDC2_W : + doLDC(ByteArray.readU16bit(code, pos + 1)); + return 3; + case Opcode.ILOAD : + return doXLOAD(INTEGER, code, pos); + case Opcode.LLOAD : + return doXLOAD(LONG, code, pos); + case Opcode.FLOAD : + return doXLOAD(FLOAT, code, pos); + case Opcode.DLOAD : + return doXLOAD(DOUBLE, code, pos); + case Opcode.ALOAD : + return doALOAD(code[pos + 1] & 0xff); + case Opcode.ILOAD_0 : + case Opcode.ILOAD_1 : + case Opcode.ILOAD_2 : + case Opcode.ILOAD_3 : + stackTypes[stackTop++] = INTEGER; + break; + case Opcode.LLOAD_0 : + case Opcode.LLOAD_1 : + case Opcode.LLOAD_2 : + case Opcode.LLOAD_3 : + stackTypes[stackTop++] = LONG; + stackTypes[stackTop++] = TOP; + break; + case Opcode.FLOAD_0 : + case Opcode.FLOAD_1 : + case Opcode.FLOAD_2 : + case Opcode.FLOAD_3 : + stackTypes[stackTop++] = FLOAT; + break; + case Opcode.DLOAD_0 : + case Opcode.DLOAD_1 : + case Opcode.DLOAD_2 : + case Opcode.DLOAD_3 : + stackTypes[stackTop++] = DOUBLE; + stackTypes[stackTop++] = TOP; + break; + case Opcode.ALOAD_0 : + case Opcode.ALOAD_1 : + case Opcode.ALOAD_2 : + case Opcode.ALOAD_3 : + reg = op - Opcode.ALOAD_0; + stackTypes[stackTop++] = localsTypes[reg]; + break; + case Opcode.IALOAD : + stackTypes[--stackTop - 1] = INTEGER; + break; + case Opcode.LALOAD : + stackTypes[stackTop - 2] = LONG; + stackTypes[stackTop - 1] = TOP; + break; + case Opcode.FALOAD : + stackTypes[--stackTop - 1] = FLOAT; + break; + case Opcode.DALOAD : + stackTypes[stackTop - 2] = DOUBLE; + stackTypes[stackTop - 1] = TOP; + break; + case Opcode.AALOAD : { + int s = --stackTop - 1; + TypeData data = stackTypes[s]; + stackTypes[s] = TypeData.ArrayElement.make(data); + break; } + case Opcode.BALOAD : + case Opcode.CALOAD : + case Opcode.SALOAD : + stackTypes[--stackTop - 1] = INTEGER; + break; + default : + throw new RuntimeException("fatal"); + } + + return 1; + } + + private void doLDC(int index) { + TypeData[] stackTypes = this.stackTypes; + int tag = cpool.getTag(index); + if (tag == ConstPool.CONST_String) + stackTypes[stackTop++] = new TypeData.ClassName("java.lang.String"); + else if (tag == ConstPool.CONST_Integer) + stackTypes[stackTop++] = INTEGER; + else if (tag == ConstPool.CONST_Float) + stackTypes[stackTop++] = FLOAT; + else if (tag == ConstPool.CONST_Long) { + stackTypes[stackTop++] = LONG; + stackTypes[stackTop++] = TOP; + } + else if (tag == ConstPool.CONST_Double) { + stackTypes[stackTop++] = DOUBLE; + stackTypes[stackTop++] = TOP; + } + else if (tag == ConstPool.CONST_Class) + stackTypes[stackTop++] = new TypeData.ClassName("java.lang.Class"); + else + throw new RuntimeException("bad LDC: " + tag); + } + + private int doXLOAD(TypeData type, byte[] code, int pos) { + int localVar = code[pos + 1] & 0xff; + return doXLOAD(localVar, type); + } + + private int doXLOAD(int localVar, TypeData type) { + stackTypes[stackTop++] = type; + if (type.is2WordType()) + stackTypes[stackTop++] = TOP; + + return 2; + } + + private int doALOAD(int localVar) { + stackTypes[stackTop++] = localsTypes[localVar]; + return 2; + } + + private int doOpcode54_95(int pos, byte[] code, int op) throws BadBytecode { + switch (op) { + case Opcode.ISTORE : + return doXSTORE(pos, code, INTEGER); + case Opcode.LSTORE : + return doXSTORE(pos, code, LONG); + case Opcode.FSTORE : + return doXSTORE(pos, code, FLOAT); + case Opcode.DSTORE : + return doXSTORE(pos, code, DOUBLE); + case Opcode.ASTORE : + return doASTORE(code[pos + 1] & 0xff); + case Opcode.ISTORE_0 : + case Opcode.ISTORE_1 : + case Opcode.ISTORE_2 : + case Opcode.ISTORE_3 : + { int var = op - Opcode.ISTORE_0; + localsTypes[var] = INTEGER; + stackTop--; } + break; + case Opcode.LSTORE_0 : + case Opcode.LSTORE_1 : + case Opcode.LSTORE_2 : + case Opcode.LSTORE_3 : + { int var = op - Opcode.LSTORE_0; + localsTypes[var] = LONG; + localsTypes[var + 1] = TOP; + stackTop -= 2; } + break; + case Opcode.FSTORE_0 : + case Opcode.FSTORE_1 : + case Opcode.FSTORE_2 : + case Opcode.FSTORE_3 : + { int var = op - Opcode.FSTORE_0; + localsTypes[var] = FLOAT; + stackTop--; } + break; + case Opcode.DSTORE_0 : + case Opcode.DSTORE_1 : + case Opcode.DSTORE_2 : + case Opcode.DSTORE_3 : + { int var = op - Opcode.DSTORE_0; + localsTypes[var] = DOUBLE; + localsTypes[var + 1] = TOP; + stackTop -= 2; } + break; + case Opcode.ASTORE_0 : + case Opcode.ASTORE_1 : + case Opcode.ASTORE_2 : + case Opcode.ASTORE_3 : + { int var = op - Opcode.ASTORE_0; + doASTORE(var); + break; } + case Opcode.IASTORE : + case Opcode.LASTORE : + case Opcode.FASTORE : + case Opcode.DASTORE : + stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3; + break; + case Opcode.AASTORE : + TypeData.ArrayElement.aastore(stackTypes[stackTop - 3], + stackTypes[stackTop - 1], + classPool); + stackTop -= 3; + break; + case Opcode.BASTORE : + case Opcode.CASTORE : + case Opcode.SASTORE : + stackTop -= 3; + break; + case Opcode.POP : + stackTop--; + break; + case Opcode.POP2 : + stackTop -= 2; + break; + case Opcode.DUP : { + int sp = stackTop; + stackTypes[sp] = stackTypes[sp - 1]; + stackTop = sp + 1; + break; } + case Opcode.DUP_X1 : + case Opcode.DUP_X2 : { + int len = op - Opcode.DUP_X1 + 2; + doDUP_XX(1, len); + int sp = stackTop; + stackTypes[sp - len] = stackTypes[sp]; + stackTop = sp + 1; + break; } + case Opcode.DUP2 : + doDUP_XX(2, 2); + stackTop += 2; + break; + case Opcode.DUP2_X1 : + case Opcode.DUP2_X2 : { + int len = op - Opcode.DUP2_X1 + 3; + doDUP_XX(2, len); + int sp = stackTop; + stackTypes[sp - len] = stackTypes[sp]; + stackTypes[sp - len + 1] = stackTypes[sp + 1]; + stackTop = sp + 2; + break; } + case Opcode.SWAP : { + int sp = stackTop - 1; + TypeData t = stackTypes[sp]; + stackTypes[sp] = stackTypes[sp - 1]; + stackTypes[sp - 1] = t; + break; } + default : + throw new RuntimeException("fatal"); + } + + return 1; + } + + private int doXSTORE(int pos, byte[] code, TypeData type) { + int index = code[pos + 1] & 0xff; + return doXSTORE(index, type); + } + + private int doXSTORE(int index, TypeData type) { + stackTop--; + localsTypes[index] = type; + if (type.is2WordType()) { + stackTop--; + localsTypes[index + 1] = TOP; + } + + return 2; + } + + private int doASTORE(int index) { + stackTop--; + // implicit upcast might be done. + localsTypes[index] = stackTypes[stackTop]; + return 2; + } + + private void doDUP_XX(int delta, int len) { + TypeData types[] = stackTypes; + int sp = stackTop - 1; + int end = sp - len; + while (sp > end) { + types[sp + delta] = types[sp]; + sp--; + } + } + + private int doOpcode96_147(int pos, byte[] code, int op) { + if (op <= Opcode.LXOR) { // IADD...LXOR + stackTop += Opcode.STACK_GROW[op]; + return 1; + } + + switch (op) { + case Opcode.IINC : + // this does not call writeLocal(). + return 3; + case Opcode.I2L : + stackTypes[stackTop - 1] = LONG; + stackTypes[stackTop] = TOP; + stackTop++; + break; + case Opcode.I2F : + stackTypes[stackTop - 1] = FLOAT; + break; + case Opcode.I2D : + stackTypes[stackTop - 1] = DOUBLE; + stackTypes[stackTop] = TOP; + stackTop++; + break; + case Opcode.L2I : + stackTypes[--stackTop - 1] = INTEGER; + break; + case Opcode.L2F : + stackTypes[--stackTop - 1] = FLOAT; + break; + case Opcode.L2D : + stackTypes[stackTop - 2] = DOUBLE; + break; + case Opcode.F2I : + stackTypes[stackTop - 1] = INTEGER; + break; + case Opcode.F2L : + stackTypes[stackTop - 1] = LONG; + stackTypes[stackTop] = TOP; + stackTop++; + break; + case Opcode.F2D : + stackTypes[stackTop - 1] = DOUBLE; + stackTypes[stackTop] = TOP; + stackTop++; + break; + case Opcode.D2I : + stackTypes[--stackTop - 1] = INTEGER; + break; + case Opcode.D2L : + stackTypes[stackTop - 2] = LONG; + break; + case Opcode.D2F : + stackTypes[--stackTop - 1] = FLOAT; + break; + case Opcode.I2B : + case Opcode.I2C : + case Opcode.I2S : + break; + default : + throw new RuntimeException("fatal"); + } + + return 1; + } + + private int doOpcode148_201(int pos, byte[] code, int op) throws BadBytecode { + switch (op) { + case Opcode.LCMP : + stackTypes[stackTop - 4] = INTEGER; + stackTop -= 3; + break; + case Opcode.FCMPL : + case Opcode.FCMPG : + stackTypes[--stackTop - 1] = INTEGER; + break; + case Opcode.DCMPL : + case Opcode.DCMPG : + stackTypes[stackTop - 4] = INTEGER; + stackTop -= 3; + break; + case Opcode.IFEQ : + case Opcode.IFNE : + case Opcode.IFLT : + case Opcode.IFGE : + case Opcode.IFGT : + case Opcode.IFLE : + stackTop--; // branch + visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); + return 3; + case Opcode.IF_ICMPEQ : + case Opcode.IF_ICMPNE : + case Opcode.IF_ICMPLT : + case Opcode.IF_ICMPGE : + case Opcode.IF_ICMPGT : + case Opcode.IF_ICMPLE : + case Opcode.IF_ACMPEQ : + case Opcode.IF_ACMPNE : + stackTop -= 2; // branch + visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); + return 3; + case Opcode.GOTO : + visitGoto(pos, code, ByteArray.readS16bit(code, pos + 1)); + return 3; // branch + case Opcode.JSR : + visitJSR(pos, code); + return 3; // branch + case Opcode.RET : + visitRET(pos, code); + return 2; + case Opcode.TABLESWITCH : { + stackTop--; // branch + int pos2 = (pos & ~3) + 8; + int low = ByteArray.read32bit(code, pos2); + int high = ByteArray.read32bit(code, pos2 + 4); + int n = high - low + 1; + visitTableSwitch(pos, code, n, pos2 + 8, ByteArray.read32bit(code, pos2 - 4)); + return n * 4 + 16 - (pos & 3); } + case Opcode.LOOKUPSWITCH : { + stackTop--; // branch + int pos2 = (pos & ~3) + 8; + int n = ByteArray.read32bit(code, pos2); + visitLookupSwitch(pos, code, n, pos2 + 4, ByteArray.read32bit(code, pos2 - 4)); + return n * 8 + 12 - (pos & 3); } + case Opcode.IRETURN : + stackTop--; + visitReturn(pos, code); + break; + case Opcode.LRETURN : + stackTop -= 2; + visitReturn(pos, code); + break; + case Opcode.FRETURN : + stackTop--; + visitReturn(pos, code); + break; + case Opcode.DRETURN : + stackTop -= 2; + visitReturn(pos, code); + break; + case Opcode.ARETURN : + stackTypes[--stackTop].setType(returnType, classPool); + visitReturn(pos, code); + break; + case Opcode.RETURN : + visitReturn(pos, code); + break; + case Opcode.GETSTATIC : + return doGetField(pos, code, false); + case Opcode.PUTSTATIC : + return doPutField(pos, code, false); + case Opcode.GETFIELD : + return doGetField(pos, code, true); + case Opcode.PUTFIELD : + return doPutField(pos, code, true); + case Opcode.INVOKEVIRTUAL : + case Opcode.INVOKESPECIAL : + return doInvokeMethod(pos, code, true); + case Opcode.INVOKESTATIC : + return doInvokeMethod(pos, code, false); + case Opcode.INVOKEINTERFACE : + return doInvokeIntfMethod(pos, code); + case Opcode.INVOKEDYNAMIC : + return doInvokeDynamic(pos, code); + case Opcode.NEW : { + int i = ByteArray.readU16bit(code, pos + 1); + stackTypes[stackTop++] + = new TypeData.UninitData(pos, cpool.getClassInfo(i)); + return 3; } + case Opcode.NEWARRAY : + return doNEWARRAY(pos, code); + case Opcode.ANEWARRAY : { + int i = ByteArray.readU16bit(code, pos + 1); + String type = cpool.getClassInfo(i).replace('.', '/'); + if (type.charAt(0) == '[') + type = "[" + type; + else + type = "[L" + type + ";"; + + stackTypes[stackTop - 1] + = new TypeData.ClassName(type); + return 3; } + case Opcode.ARRAYLENGTH : + stackTypes[stackTop - 1].setType("[Ljava.lang.Object;", classPool); + stackTypes[stackTop - 1] = INTEGER; + break; + case Opcode.ATHROW : + stackTypes[--stackTop].setType("java.lang.Throwable", classPool); + visitThrow(pos, code); + break; + case Opcode.CHECKCAST : { + // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool); + int i = ByteArray.readU16bit(code, pos + 1); + String type = cpool.getClassInfo(i); + if (type.charAt(0) == '[') + type = type.replace('.', '/'); // getClassInfo() may return "[java.lang.Object;". + + stackTypes[stackTop - 1] = new TypeData.ClassName(type); + return 3; } + case Opcode.INSTANCEOF : + // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool); + stackTypes[stackTop - 1] = INTEGER; + return 3; + case Opcode.MONITORENTER : + case Opcode.MONITOREXIT : + stackTop--; + // TypeData.setType(stackTypes[stackTop], "java.lang.Object", classPool); + break; + case Opcode.WIDE : + return doWIDE(pos, code); + case Opcode.MULTIANEWARRAY : + return doMultiANewArray(pos, code); + case Opcode.IFNULL : + case Opcode.IFNONNULL : + stackTop--; // branch + visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); + return 3; + case Opcode.GOTO_W : + visitGoto(pos, code, ByteArray.read32bit(code, pos + 1)); + return 5; // branch + case Opcode.JSR_W : + visitJSR(pos, code); + return 5; + } + return 1; + } + + private int doWIDE(int pos, byte[] code) throws BadBytecode { + int op = code[pos + 1] & 0xff; + switch (op) { + case Opcode.ILOAD : + doWIDE_XLOAD(pos, code, INTEGER); + break; + case Opcode.LLOAD : + doWIDE_XLOAD(pos, code, LONG); + break; + case Opcode.FLOAD : + doWIDE_XLOAD(pos, code, FLOAT); + break; + case Opcode.DLOAD : + doWIDE_XLOAD(pos, code, DOUBLE); + break; + case Opcode.ALOAD : { + int index = ByteArray.readU16bit(code, pos + 2); + doALOAD(index); + break; } + case Opcode.ISTORE : + doWIDE_STORE(pos, code, INTEGER); + break; + case Opcode.LSTORE : + doWIDE_STORE(pos, code, LONG); + break; + case Opcode.FSTORE : + doWIDE_STORE(pos, code, FLOAT); + break; + case Opcode.DSTORE : + doWIDE_STORE(pos, code, DOUBLE); + break; + case Opcode.ASTORE : { + int index = ByteArray.readU16bit(code, pos + 2); + doASTORE(index); + break; } + case Opcode.IINC : + // this does not call writeLocal(). + return 6; + case Opcode.RET : + visitRET(pos, code); + break; + default : + throw new RuntimeException("bad WIDE instruction: " + op); + } + + return 4; + } + + private void doWIDE_XLOAD(int pos, byte[] code, TypeData type) { + int index = ByteArray.readU16bit(code, pos + 2); + doXLOAD(index, type); + } + + private void doWIDE_STORE(int pos, byte[] code, TypeData type) { + int index = ByteArray.readU16bit(code, pos + 2); + doXSTORE(index, type); + } + + private int doPutField(int pos, byte[] code, boolean notStatic) throws BadBytecode { + int index = ByteArray.readU16bit(code, pos + 1); + String desc = cpool.getFieldrefType(index); + stackTop -= Descriptor.dataSize(desc); + char c = desc.charAt(0); + if (c == 'L') + stackTypes[stackTop].setType(getFieldClassName(desc, 0), classPool); + else if (c == '[') + stackTypes[stackTop].setType(desc, classPool); + + setFieldTarget(notStatic, index); + return 3; + } + + private int doGetField(int pos, byte[] code, boolean notStatic) throws BadBytecode { + int index = ByteArray.readU16bit(code, pos + 1); + setFieldTarget(notStatic, index); + String desc = cpool.getFieldrefType(index); + pushMemberType(desc); + return 3; + } + + private void setFieldTarget(boolean notStatic, int index) throws BadBytecode { + if (notStatic) { + String className = cpool.getFieldrefClassName(index); + stackTypes[--stackTop].setType(className, classPool); + } + } + + private int doNEWARRAY(int pos, byte[] code) { + int s = stackTop - 1; + String type; + switch (code[pos + 1] & 0xff) { + case Opcode.T_BOOLEAN : + type = "[Z"; + break; + case Opcode.T_CHAR : + type = "[C"; + break; + case Opcode.T_FLOAT : + type = "[F"; + break; + case Opcode.T_DOUBLE : + type = "[D"; + break; + case Opcode.T_BYTE : + type = "[B"; + break; + case Opcode.T_SHORT : + type = "[S"; + break; + case Opcode.T_INT : + type = "[I"; + break; + case Opcode.T_LONG : + type = "[J"; + break; + default : + throw new RuntimeException("bad newarray"); + } + + stackTypes[s] = new TypeData.ClassName(type); + return 2; + } + + private int doMultiANewArray(int pos, byte[] code) { + int i = ByteArray.readU16bit(code, pos + 1); + int dim = code[pos + 3] & 0xff; + stackTop -= dim - 1; + + String type = cpool.getClassInfo(i).replace('.', '/'); + stackTypes[stackTop - 1] = new TypeData.ClassName(type); + return 4; + } + + private int doInvokeMethod(int pos, byte[] code, boolean notStatic) throws BadBytecode { + int i = ByteArray.readU16bit(code, pos + 1); + String desc = cpool.getMethodrefType(i); + checkParamTypes(desc, 1); + if (notStatic) { + String className = cpool.getMethodrefClassName(i); + TypeData target = stackTypes[--stackTop]; + if (target instanceof TypeData.UninitTypeVar && target.isUninit()) + constructorCalled(target, ((TypeData.UninitTypeVar)target).offset()); + else if (target instanceof TypeData.UninitData) + constructorCalled(target, ((TypeData.UninitData)target).offset()); + + target.setType(className, classPool); + } + + pushMemberType(desc); + return 3; + } + + /* This is a constructor call on an uninitialized object. + * Sets flags of other references to that object. + * + * @param offset the offset where the object has been created. + */ + private void constructorCalled(TypeData target, int offset) { + target.constructorCalled(offset); + for (int i = 0; i < stackTop; i++) + stackTypes[i].constructorCalled(offset); + + for (int i = 0; i < localsTypes.length; i++) + localsTypes[i].constructorCalled(offset); + } + + private int doInvokeIntfMethod(int pos, byte[] code) throws BadBytecode { + int i = ByteArray.readU16bit(code, pos + 1); + String desc = cpool.getInterfaceMethodrefType(i); + checkParamTypes(desc, 1); + String className = cpool.getInterfaceMethodrefClassName(i); + stackTypes[--stackTop].setType(className, classPool); + pushMemberType(desc); + return 5; + } + + private int doInvokeDynamic(int pos, byte[] code) throws BadBytecode { + int i = ByteArray.readU16bit(code, pos + 1); + String desc = cpool.getInvokeDynamicType(i); + checkParamTypes(desc, 1); + + // assume CosntPool#REF_invokeStatic + /* TypeData target = stackTypes[--stackTop]; + if (target instanceof TypeData.UninitTypeVar && target.isUninit()) + constructorCalled((TypeData.UninitTypeVar)target); + */ + + pushMemberType(desc); + return 5; + } + + private void pushMemberType(String descriptor) { + int top = 0; + if (descriptor.charAt(0) == '(') { + top = descriptor.indexOf(')') + 1; + if (top < 1) + throw new IndexOutOfBoundsException("bad descriptor: " + + descriptor); + } + + TypeData[] types = stackTypes; + int index = stackTop; + switch (descriptor.charAt(top)) { + case '[' : + types[index] = new TypeData.ClassName(descriptor.substring(top)); + break; + case 'L' : + types[index] = new TypeData.ClassName(getFieldClassName(descriptor, top)); + break; + case 'J' : + types[index] = LONG; + types[index + 1] = TOP; + stackTop += 2; + return; + case 'F' : + types[index] = FLOAT; + break; + case 'D' : + types[index] = DOUBLE; + types[index + 1] = TOP; + stackTop += 2; + return; + case 'V' : + return; + default : // C, B, S, I, Z + types[index] = INTEGER; + break; + } + + stackTop++; + } + + private static String getFieldClassName(String desc, int index) { + return desc.substring(index + 1, desc.length() - 1).replace('/', '.'); + } + + private void checkParamTypes(String desc, int i) throws BadBytecode { + char c = desc.charAt(i); + if (c == ')') + return; + + int k = i; + boolean array = false; + while (c == '[') { + array = true; + c = desc.charAt(++k); + } + + if (c == 'L') { + k = desc.indexOf(';', k) + 1; + if (k <= 0) + throw new IndexOutOfBoundsException("bad descriptor"); + } + else + k++; + + checkParamTypes(desc, k); + if (!array && (c == 'J' || c == 'D')) + stackTop -= 2; + else + stackTop--; + + if (array) + stackTypes[stackTop].setType(desc.substring(i, k), classPool); + else if (c == 'L') + stackTypes[stackTop].setType(desc.substring(i + 1, k - 1).replace('/', '.'), + classPool); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java new file mode 100644 index 0000000..afab68c --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java @@ -0,0 +1,785 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.stackmap; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.bytecode.StackMapTable; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import java.util.HashSet; +import java.util.Iterator; +import java.util.ArrayList; + +public abstract class TypeData { + /* Memo: + * array type is a subtype of Cloneable and Serializable + */ + + public static TypeData[] make(int size) { + TypeData[] array = new TypeData[size]; + for (int i = 0; i < size; i++) + array[i] = TypeTag.TOP; + + return array; + } + + protected TypeData() {} + + /** + * Sets the type name of this object type. If the given type name is + * a subclass of the current type name, then the given name becomes + * the name of this object type. + * + * @param className dot-separated name unless the type is an array type. + */ + private static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode { + td.setType(className, cp); + } + + public abstract int getTypeTag(); + public abstract int getTypeData(ConstPool cp); + + public TypeData join() { return new TypeVar(this); } + + /** + * If the type is a basic type, this method normalizes the type + * and returns a BasicType object. Otherwise, it returns null. + */ + public abstract BasicType isBasicType(); + + public abstract boolean is2WordType(); + + /** + * Returns false if getName() returns a valid type name. + */ + public boolean isNullType() { return false; } + + public boolean isUninit() { return false; } + + public abstract boolean eq(TypeData d); + + public abstract String getName(); + public abstract void setType(String s, ClassPool cp) throws BadBytecode; + + // depth-first search + public int dfs(ArrayList order, int index, ClassPool cp) + throws NotFoundException + { + return index; + } + + /** + * Returns this if it is a TypeVar or a TypeVar that this + * type depends on. Otherwise, this method returns null. + * It is used by dfs(). + */ + protected TypeVar toTypeVar() { return null; } + + // see UninitTypeVar and UninitData + public void constructorCalled(int offset) {} + + /** + * Primitive types. + */ + protected static class BasicType extends TypeData { + private String name; + private int typeTag; + + public BasicType(String type, int tag) { + name = type; + typeTag = tag; + } + + public int getTypeTag() { return typeTag; } + public int getTypeData(ConstPool cp) { return 0; } + + public TypeData join() { + if (this == TypeTag.TOP) + return this; + else + return super.join(); + } + + public BasicType isBasicType() { return this; } + + public boolean is2WordType() { + return typeTag == StackMapTable.LONG + || typeTag == StackMapTable.DOUBLE; + } + + public boolean eq(TypeData d) { return this == d; } + + public String getName() { + return name; + } + + public void setType(String s, ClassPool cp) throws BadBytecode { + throw new BadBytecode("conflict: " + name + " and " + s); + } + + public String toString() { return name; } + } + + // a type variable + public static abstract class AbsTypeVar extends TypeData { + public AbsTypeVar() {} + public abstract void merge(TypeData t); + public int getTypeTag() { return StackMapTable.OBJECT; } + + public int getTypeData(ConstPool cp) { + return cp.addClassInfo(getName()); + } + + public boolean eq(TypeData d) { return getName().equals(d.getName()); } + } + + /* a type variable representing a class type or a basic type. + */ + public static class TypeVar extends AbsTypeVar { + protected ArrayList lowers; // lower bounds of this type. ArrayList + protected ArrayList usedBy; // reverse relations of lowers + protected ArrayList uppers; // upper bounds of this type. + protected String fixedType; + private boolean is2WordType; // cache + + public TypeVar(TypeData t) { + uppers = null; + lowers = new ArrayList(2); + usedBy = new ArrayList(2); + merge(t); + fixedType = null; + is2WordType = t.is2WordType(); + } + + public String getName() { + if (fixedType == null) + return ((TypeData)lowers.get(0)).getName(); + else + return fixedType; + } + + public BasicType isBasicType() { + if (fixedType == null) + return ((TypeData)lowers.get(0)).isBasicType(); + else + return null; + } + + public boolean is2WordType() { + if (fixedType == null) { + return is2WordType; + // return ((TypeData)lowers.get(0)).is2WordType(); + } + else + return false; + } + + public boolean isNullType() { + if (fixedType == null) + return ((TypeData)lowers.get(0)).isNullType(); + else + return false; + } + + public boolean isUninit() { + if (fixedType == null) + return ((TypeData)lowers.get(0)).isUninit(); + else + return false; + } + + public void merge(TypeData t) { + lowers.add(t); + if (t instanceof TypeVar) + ((TypeVar)t).usedBy.add(this); + } + + public int getTypeTag() { + /* If fixedType is null after calling dfs(), then this + type is NULL, Uninit, or a basic type. So call + getTypeTag() on the first element of lowers. */ + if (fixedType == null) + return ((TypeData)lowers.get(0)).getTypeTag(); + else + return super.getTypeTag(); + } + + public int getTypeData(ConstPool cp) { + if (fixedType == null) + return ((TypeData)lowers.get(0)).getTypeData(cp); + else + return super.getTypeData(cp); + } + + public void setType(String typeName, ClassPool cp) throws BadBytecode { + if (uppers == null) + uppers = new ArrayList(); + + uppers.add(typeName); + } + + protected TypeVar toTypeVar() { return this; } + + private int visited = 0; + private int smallest = 0; + private boolean inList = false; + + // depth-first serach + public int dfs(ArrayList preOrder, int index, ClassPool cp) throws NotFoundException { + if (visited > 0) + return index; // MapMaker.make() may call an already visited node. + + visited = smallest = ++index; + preOrder.add(this); + inList = true; + int n = lowers.size(); + for (int i = 0; i < n; i++) { + TypeVar child = ((TypeData)lowers.get(i)).toTypeVar(); + if (child != null) + if (child.visited == 0) { + index = child.dfs(preOrder, index, cp); + if (child.smallest < smallest) + smallest = child.smallest; + } + else if (child.inList) + if (child.visited < smallest) + smallest = child.visited; + } + + if (visited == smallest) { + ArrayList scc = new ArrayList(); // strongly connected component + TypeVar cv; + do { + cv = (TypeVar)preOrder.remove(preOrder.size() - 1); + cv.inList = false; + scc.add(cv); + } while (cv != this); + fixTypes(scc, cp); + } + + return index; + } + + private void fixTypes(ArrayList scc, ClassPool cp) throws NotFoundException { + HashSet lowersSet = new HashSet(); + boolean isBasicType = false; + TypeData kind = null; + int size = scc.size(); + for (int i = 0; i < size; i++) { + ArrayList tds = ((TypeVar)scc.get(i)).lowers; + int size2 = tds.size(); + for (int j = 0; j < size2; j++) { + TypeData d = (TypeData)tds.get(j); + BasicType bt = d.isBasicType(); + if (kind == null) { + if (bt == null) { + isBasicType = false; + kind = d; + /* If scc has only an UninitData, fixedType is kept null. + So lowerSet must be empty. If scc has not only an UninitData + but also another TypeData, an error must be thrown but this + error detection has not been implemented. */ + if (d.isUninit()) + break; + } + else { + isBasicType = true; + kind = bt; + } + } + else { + if ((bt == null && isBasicType) + || (bt != null && kind != bt)) { + isBasicType = true; + kind = TypeTag.TOP; + break; + } + } + + if (bt == null && !d.isNullType()) + lowersSet.add(d.getName()); + } + } + + if (isBasicType) { + for (int i = 0; i < size; i++) { + TypeVar cv = (TypeVar)scc.get(i); + cv.lowers.clear(); + cv.lowers.add(kind); + is2WordType = kind.is2WordType(); + } + } + else { + String typeName = fixTypes2(scc, lowersSet, cp); + for (int i = 0; i < size; i++) { + TypeVar cv = (TypeVar)scc.get(i); + cv.fixedType = typeName; + } + } + } + + private String fixTypes2(ArrayList scc, HashSet lowersSet, ClassPool cp) throws NotFoundException { + Iterator it = lowersSet.iterator(); + if (lowersSet.size() == 0) + return null; // only NullType + else if (lowersSet.size() == 1) + return (String)it.next(); + else { + CtClass cc = cp.get((String)it.next()); + while (it.hasNext()) + cc = commonSuperClassEx(cc, cp.get((String)it.next())); + + if (cc.getSuperclass() == null || isObjectArray(cc)) + cc = fixByUppers(scc, cp, new HashSet(), cc); + + if (cc.isArray()) + return Descriptor.toJvmName(cc); + else + return cc.getName(); + } + } + + private static boolean isObjectArray(CtClass cc) throws NotFoundException { + return cc.isArray() && cc.getComponentType().getSuperclass() == null; + } + + private CtClass fixByUppers(ArrayList users, ClassPool cp, HashSet visited, CtClass type) + throws NotFoundException + { + if (users == null) + return type; + + int size = users.size(); + for (int i = 0; i < size; i++) { + TypeVar t = (TypeVar)users.get(i); + if (!visited.add(t)) + return type; + + if (t.uppers != null) { + int s = t.uppers.size(); + for (int k = 0; k < s; k++) { + CtClass cc = cp.get((String)t.uppers.get(k)); + if (cc.subtypeOf(type)) + type = cc; + } + } + + type = fixByUppers(t.usedBy, cp, visited, type); + } + + return type; + } + } + + /** + * Finds the most specific common super class of the given classes + * by considering array types. + */ + public static CtClass commonSuperClassEx(CtClass one, CtClass two) throws NotFoundException { + if (one == two) + return one; + else if (one.isArray() && two.isArray()) { + CtClass ele1 = one.getComponentType(); + CtClass ele2 = two.getComponentType(); + CtClass element = commonSuperClassEx(ele1, ele2); + if (element == ele1) + return one; + else if (element == ele2) + return two; + else + return one.getClassPool().get(element == null ? "java.lang.Object" + : element.getName() + "[]"); + } + else if (one.isPrimitive() || two.isPrimitive()) + return null; // TOP + else if (one.isArray() || two.isArray()) // but !(one.isArray() && two.isArray()) + return one.getClassPool().get("java.lang.Object"); + else + return commonSuperClass(one, two); + } + + /** + * Finds the most specific common super class of the given classes. + * This method is a copy from javassist.bytecode.analysis.Type. + */ + public static CtClass commonSuperClass(CtClass one, CtClass two) throws NotFoundException { + CtClass deep = one; + CtClass shallow = two; + CtClass backupShallow = shallow; + CtClass backupDeep = deep; + + // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly + for (;;) { + // In case we get lucky, and find a match early + if (eq(deep, shallow) && deep.getSuperclass() != null) + return deep; + + CtClass deepSuper = deep.getSuperclass(); + CtClass shallowSuper = shallow.getSuperclass(); + + if (shallowSuper == null) { + // right, now reset shallow + shallow = backupShallow; + break; + } + + if (deepSuper == null) { + // wrong, swap them, since deep is now useless, its our tmp before we swap it + deep = backupDeep; + backupDeep = backupShallow; + backupShallow = deep; + + deep = shallow; + shallow = backupShallow; + break; + } + + deep = deepSuper; + shallow = shallowSuper; + } + + // Phase 2 - Move deepBackup up by (deep end - deep) + for (;;) { + deep = deep.getSuperclass(); + if (deep == null) + break; + + backupDeep = backupDeep.getSuperclass(); + } + + deep = backupDeep; + + // Phase 3 - The hierarchy positions are now aligned + // The common super class is easy to find now + while (!eq(deep, shallow)) { + deep = deep.getSuperclass(); + shallow = shallow.getSuperclass(); + } + + return deep; + } + + static boolean eq(CtClass one, CtClass two) { + return one == two || (one != null && two != null && one.getName().equals(two.getName())); + } + + public static void aastore(TypeData array, TypeData value, ClassPool cp) throws BadBytecode { + if (array instanceof AbsTypeVar) + if (!value.isNullType()) + ((AbsTypeVar)array).merge(ArrayType.make(value)); + + if (value instanceof AbsTypeVar) + if (array instanceof AbsTypeVar) + ArrayElement.make(array); // should call value.setType() later. + else if (array instanceof ClassName) { + if (!array.isNullType()) { + String type = ArrayElement.typeName(array.getName()); + value.setType(type, cp); + } + } + else + throw new BadBytecode("bad AASTORE: " + array); + } + + /* A type variable representing an array type. + * It is a decorator of another type variable. + */ + public static class ArrayType extends AbsTypeVar { + private AbsTypeVar element; + + private ArrayType(AbsTypeVar elementType) { + element = elementType; + } + + static TypeData make(TypeData element) throws BadBytecode { + if (element instanceof ArrayElement) + return ((ArrayElement)element).arrayType(); + else if (element instanceof AbsTypeVar) + return new ArrayType((AbsTypeVar)element); + else if (element instanceof ClassName) + if (!element.isNullType()) + return new ClassName(typeName(element.getName())); + + throw new BadBytecode("bad AASTORE: " + element); + } + + public void merge(TypeData t) { + try { + if (!t.isNullType()) + element.merge(ArrayElement.make(t)); + } + catch (BadBytecode e) { + // never happens + throw new RuntimeException("fatal: " + e); + } + } + + public String getName() { + return typeName(element.getName()); + } + + public AbsTypeVar elementType() { return element; } + + public BasicType isBasicType() { return null; } + public boolean is2WordType() { return false; } + + /* elementType must be a class name. Basic type names + * are not allowed. + */ + public static String typeName(String elementType) { + if (elementType.charAt(0) == '[') + return "[" + elementType; + else + return "[L" + elementType.replace('.', '/') + ";"; + } + + public void setType(String s, ClassPool cp) throws BadBytecode { + element.setType(ArrayElement.typeName(s), cp); + } + + protected TypeVar toTypeVar() { return element.toTypeVar(); } + + public int dfs(ArrayList order, int index, ClassPool cp) throws NotFoundException { + return element.dfs(order, index, cp); + } + } + + /* A type variable representing an array-element type. + * It is a decorator of another type variable. + */ + public static class ArrayElement extends AbsTypeVar { + private AbsTypeVar array; + + private ArrayElement(AbsTypeVar a) { // a is never null + array = a; + } + + public static TypeData make(TypeData array) throws BadBytecode { + if (array instanceof ArrayType) + return ((ArrayType)array).elementType(); + else if (array instanceof AbsTypeVar) + return new ArrayElement((AbsTypeVar)array); + else if (array instanceof ClassName) + if (!array.isNullType()) + return new ClassName(typeName(array.getName())); + + throw new BadBytecode("bad AASTORE: " + array); + } + + public void merge(TypeData t) { + try { + if (!t.isNullType()) + array.merge(ArrayType.make(t)); + } + catch (BadBytecode e) { + // never happens + throw new RuntimeException("fatal: " + e); + } + } + + public String getName() { + return typeName(array.getName()); + } + + public AbsTypeVar arrayType() { return array; } + + /* arrayType must be a class name. Basic type names are + * not allowed. + */ + + public BasicType isBasicType() { return null; } + + public boolean is2WordType() { return false; } + + private static String typeName(String arrayType) { + if (arrayType.length() > 1 && arrayType.charAt(0) == '[') { + char c = arrayType.charAt(1); + if (c == 'L') + return arrayType.substring(2, arrayType.length() - 1).replace('/', '.'); + else if (c == '[') + return arrayType.substring(1); + } + + return "java.lang.Object"; // the array type may be NullType + } + + public void setType(String s, ClassPool cp) throws BadBytecode { + array.setType(ArrayType.typeName(s), cp); + } + + protected TypeVar toTypeVar() { return array.toTypeVar(); } + + public int dfs(ArrayList order, int index, ClassPool cp) throws NotFoundException { + return array.dfs(order, index, cp); + } + } + + public static class UninitTypeVar extends AbsTypeVar { + protected TypeData type; // UninitData or TOP + + public UninitTypeVar(UninitData t) { type = t; } + public int getTypeTag() { return type.getTypeTag(); } + public int getTypeData(ConstPool cp) { return type.getTypeData(cp); } + public BasicType isBasicType() { return type.isBasicType(); } + public boolean is2WordType() { return type.is2WordType(); } + public boolean isUninit() { return type.isUninit(); } + public boolean eq(TypeData d) { return type.eq(d); } + public String getName() { return type.getName(); } + + protected TypeVar toTypeVar() { return null; } + public TypeData join() { return type.join(); } + + public void setType(String s, ClassPool cp) throws BadBytecode { + type.setType(s, cp); + } + + public void merge(TypeData t) { + if (!t.eq(type)) + type = TypeTag.TOP; + } + + public void constructorCalled(int offset) { + type.constructorCalled(offset); + } + + public int offset() { + if (type instanceof UninitData) + return ((UninitData)type).offset; + else // if type == TypeTag.TOP + throw new RuntimeException("not available"); + } + } + + /** + * Type data for OBJECT. + */ + public static class ClassName extends TypeData { + private String name; // dot separated. + + public ClassName(String n) { + name = n; + } + + public String getName() { + return name; + } + + public BasicType isBasicType() { return null; } + + public boolean is2WordType() { return false; } + + public int getTypeTag() { return StackMapTable.OBJECT; } + + public int getTypeData(ConstPool cp) { + return cp.addClassInfo(getName()); + } + + public boolean eq(TypeData d) { return name.equals(d.getName()); } + + public void setType(String typeName, ClassPool cp) throws BadBytecode {} + } + + /** + * Type data for NULL or OBJECT. + * The types represented by the instances of this class are + * initially NULL but will be OBJECT. + */ + public static class NullType extends ClassName { + public NullType() { + super("null-type"); // type name + } + + public int getTypeTag() { + return StackMapTable.NULL; + } + + public boolean isNullType() { return true; } + public int getTypeData(ConstPool cp) { return 0; } + } + + /** + * Type data for UNINIT. + */ + public static class UninitData extends ClassName { + int offset; + boolean initialized; + + UninitData(int offset, String className) { + super(className); + this.offset = offset; + this.initialized = false; + } + + public UninitData copy() { return new UninitData(offset, getName()); } + + public int getTypeTag() { + return StackMapTable.UNINIT; + } + + public int getTypeData(ConstPool cp) { + return offset; + } + + public TypeData join() { + if (initialized) + return new TypeVar(new ClassName(getName())); + else + return new UninitTypeVar(copy()); + } + + public boolean isUninit() { return true; } + + public boolean eq(TypeData d) { + if (d instanceof UninitData) { + UninitData ud = (UninitData)d; + return offset == ud.offset && getName().equals(ud.getName()); + } + else + return false; + } + + public String toString() { return "uninit:" + getName() + "@" + offset; } + + public int offset() { return offset; } + + public void constructorCalled(int offset) { + if (offset == this.offset) + initialized = true; + } + } + + public static class UninitThis extends UninitData { + UninitThis(String className) { + super(-1, className); + } + + public UninitData copy() { return new UninitThis(getName()); } + + public int getTypeTag() { + return StackMapTable.THIS; + } + + public int getTypeData(ConstPool cp) { + return 0; + } + + public String toString() { return "uninit:this"; } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java new file mode 100644 index 0000000..89aa4e3 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java @@ -0,0 +1,30 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.stackmap; + +import com.wenshuo.agent.javassist.bytecode.StackMapTable; + +public interface TypeTag { + String TOP_TYPE = "*top*"; + TypeData TOP = new TypeData.BasicType(TOP_TYPE, StackMapTable.TOP); + TypeData INTEGER = new TypeData.BasicType("int", StackMapTable.INTEGER); + TypeData FLOAT = new TypeData.BasicType("float", StackMapTable.FLOAT); + TypeData DOUBLE = new TypeData.BasicType("double", StackMapTable.DOUBLE); + TypeData LONG = new TypeData.BasicType("long", StackMapTable.LONG); + + // and NULL, THIS, OBJECT, UNINIT +} diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java new file mode 100644 index 0000000..631bfbd --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java @@ -0,0 +1,234 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.bytecode.stackmap; + +import com.wenshuo.agent.javassist.bytecode.*; + +public class TypedBlock extends BasicBlock { + public int stackTop, numLocals; + // localsTypes is set to non-null when this block is first visited by a MapMaker. + // see alreadySet(). + public TypeData[] localsTypes; + public TypeData[] stackTypes; + + /** + * Divides the method body into basic blocks. + * The type information of the first block is initialized. + * + * @param optimize if it is true and the method does not include + * branches, this method returns null. + */ + public static TypedBlock[] makeBlocks(MethodInfo minfo, CodeAttribute ca, + boolean optimize) + throws BadBytecode + { + TypedBlock[] blocks = (TypedBlock[])new Maker().make(minfo); + if (optimize && blocks.length < 2) + if (blocks.length == 0 || blocks[0].incoming == 0) + return null; + + ConstPool pool = minfo.getConstPool(); + boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0; + blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(), + pool.getClassName(), minfo.getDescriptor(), + isStatic, minfo.isConstructor()); + return blocks; + } + + protected TypedBlock(int pos) { + super(pos); + localsTypes = null; + } + + protected void toString2(StringBuffer sbuf) { + super.toString2(sbuf); + sbuf.append(",\n stack={"); + printTypes(sbuf, stackTop, stackTypes); + sbuf.append("}, locals={"); + printTypes(sbuf, numLocals, localsTypes); + sbuf.append('}'); + } + + private void printTypes(StringBuffer sbuf, int size, + TypeData[] types) { + if (types == null) + return; + + for (int i = 0; i < size; i++) { + if (i > 0) + sbuf.append(", "); + + TypeData td = types[i]; + sbuf.append(td == null ? "<>" : td.toString()); + } + } + + public boolean alreadySet() { + return localsTypes != null; + } + + public void setStackMap(int st, TypeData[] stack, int nl, TypeData[] locals) + throws BadBytecode + { + stackTop = st; + stackTypes = stack; + numLocals = nl; + localsTypes = locals; + } + + /* + * Computes the correct value of numLocals. + */ + public void resetNumLocals() { + if (localsTypes != null) { + int nl = localsTypes.length; + while (nl > 0 && localsTypes[nl - 1].isBasicType() == TypeTag.TOP) { + if (nl > 1) { + if (localsTypes[nl - 2].is2WordType()) + break; + } + + --nl; + } + + numLocals = nl; + } + } + + public static class Maker extends BasicBlock.Maker { + protected BasicBlock makeBlock(int pos) { + return new TypedBlock(pos); + } + + protected BasicBlock[] makeArray(int size) { + return new TypedBlock[size]; + } + } + + /** + * Initializes the first block by the given method descriptor. + * + * @param block the first basic block that this method initializes. + * @param className a dot-separated fully qualified class name. + * For example, javassist.bytecode.stackmap.BasicBlock. + * @param methodDesc method descriptor. + * @param isStatic true if the method is a static method. + * @param isConstructor true if the method is a constructor. + */ + void initFirstBlock(int maxStack, int maxLocals, String className, + String methodDesc, boolean isStatic, boolean isConstructor) + throws BadBytecode + { + if (methodDesc.charAt(0) != '(') + throw new BadBytecode("no method descriptor: " + methodDesc); + + stackTop = 0; + stackTypes = TypeData.make(maxStack); + TypeData[] locals = TypeData.make(maxLocals); + if (isConstructor) + locals[0] = new TypeData.UninitThis(className); + else if (!isStatic) + locals[0] = new TypeData.ClassName(className); + + int n = isStatic ? -1 : 0; + int i = 1; + try { + while ((i = descToTag(methodDesc, i, ++n, locals)) > 0) + if (locals[n].is2WordType()) + locals[++n] = TypeTag.TOP; + } + catch (StringIndexOutOfBoundsException e) { + throw new BadBytecode("bad method descriptor: " + + methodDesc); + } + + numLocals = n; + localsTypes = locals; + } + + private static int descToTag(String desc, int i, + int n, TypeData[] types) + throws BadBytecode + { + int i0 = i; + int arrayDim = 0; + char c = desc.charAt(i); + if (c == ')') + return 0; + + while (c == '[') { + ++arrayDim; + c = desc.charAt(++i); + } + + if (c == 'L') { + int i2 = desc.indexOf(';', ++i); + if (arrayDim > 0) + types[n] = new TypeData.ClassName(desc.substring(i0, ++i2)); + else + types[n] = new TypeData.ClassName(desc.substring(i0 + 1, ++i2 - 1) + .replace('/', '.')); + return i2; + } + else if (arrayDim > 0) { + types[n] = new TypeData.ClassName(desc.substring(i0, ++i)); + return i; + } + else { + TypeData t = toPrimitiveTag(c); + if (t == null) + throw new BadBytecode("bad method descriptor: " + desc); + + types[n] = t; + return i + 1; + } + } + + private static TypeData toPrimitiveTag(char c) { + switch (c) { + case 'Z' : + case 'C' : + case 'B' : + case 'S' : + case 'I' : + return TypeTag.INTEGER; + case 'J' : + return TypeTag.LONG; + case 'F' : + return TypeTag.FLOAT; + case 'D' : + return TypeTag.DOUBLE; + case 'V' : + default : + return null; + } + } + + public static String getRetType(String desc) { + int i = desc.indexOf(')'); + if (i < 0) + return "java.lang.Object"; + + char c = desc.charAt(i + 1); + if (c == '[') + return desc.substring(i + 1); + else if (c == 'L') + return desc.substring(i + 2, desc.length() - 1).replace('/', '.'); + else + return "java.lang.Object"; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/AccessorMaker.java b/src/main/java/com/wenshuo/agent/javassist/compiler/AccessorMaker.java new file mode 100644 index 0000000..f35025f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/AccessorMaker.java @@ -0,0 +1,260 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import java.util.HashMap; + +/** + * AccessorMaker maintains accessors to private members of an enclosing + * class. It is necessary for compiling a method in an inner class. + */ +public class AccessorMaker { + private CtClass clazz; + private int uniqueNumber; + private HashMap accessors; + + static final String lastParamType = "javassist.runtime.Inner"; + + public AccessorMaker(CtClass c) { + clazz = c; + uniqueNumber = 1; + accessors = new HashMap(); + } + + public String getConstructor(CtClass c, String desc, MethodInfo orig) + throws CompileError + { + String key = ":" + desc; + String consDesc = (String)accessors.get(key); + if (consDesc != null) + return consDesc; // already exists. + + consDesc = Descriptor.appendParameter(lastParamType, desc); + ClassFile cf = clazz.getClassFile(); // turn on the modified flag. + try { + ConstPool cp = cf.getConstPool(); + ClassPool pool = clazz.getClassPool(); + MethodInfo minfo + = new MethodInfo(cp, MethodInfo.nameInit, consDesc); + minfo.setAccessFlags(0); + minfo.addAttribute(new SyntheticAttribute(cp)); + ExceptionsAttribute ea = orig.getExceptionsAttribute(); + if (ea != null) + minfo.addAttribute(ea.copy(cp, null)); + + CtClass[] params = Descriptor.getParameterTypes(desc, pool); + Bytecode code = new Bytecode(cp); + code.addAload(0); + int regno = 1; + for (int i = 0; i < params.length; ++i) + regno += code.addLoad(regno, params[i]); + code.setMaxLocals(regno + 1); // the last parameter is added. + code.addInvokespecial(clazz, MethodInfo.nameInit, desc); + + code.addReturn(null); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + } + catch (CannotCompileException e) { + throw new CompileError(e); + } + catch (NotFoundException e) { + throw new CompileError(e); + } + + accessors.put(key, consDesc); + return consDesc; + } + + /** + * Returns the name of the method for accessing a private method. + * + * @param name the name of the private method. + * @param desc the descriptor of the private method. + * @param accDesc the descriptor of the accessor method. The first + * parameter type is clazz. + * If the private method is static, + * accDesc must be identical to desc. + * + * @param orig the method info of the private method. + * @return + */ + public String getMethodAccessor(String name, String desc, String accDesc, + MethodInfo orig) + throws CompileError + { + String key = name + ":" + desc; + String accName = (String)accessors.get(key); + if (accName != null) + return accName; // already exists. + + ClassFile cf = clazz.getClassFile(); // turn on the modified flag. + accName = findAccessorName(cf); + try { + ConstPool cp = cf.getConstPool(); + ClassPool pool = clazz.getClassPool(); + MethodInfo minfo + = new MethodInfo(cp, accName, accDesc); + minfo.setAccessFlags(AccessFlag.STATIC); + minfo.addAttribute(new SyntheticAttribute(cp)); + ExceptionsAttribute ea = orig.getExceptionsAttribute(); + if (ea != null) + minfo.addAttribute(ea.copy(cp, null)); + + CtClass[] params = Descriptor.getParameterTypes(accDesc, pool); + int regno = 0; + Bytecode code = new Bytecode(cp); + for (int i = 0; i < params.length; ++i) + regno += code.addLoad(regno, params[i]); + + code.setMaxLocals(regno); + if (desc == accDesc) + code.addInvokestatic(clazz, name, desc); + else + code.addInvokevirtual(clazz, name, desc); + + code.addReturn(Descriptor.getReturnType(desc, pool)); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + } + catch (CannotCompileException e) { + throw new CompileError(e); + } + catch (NotFoundException e) { + throw new CompileError(e); + } + + accessors.put(key, accName); + return accName; + } + + /** + * Returns the method_info representing the added getter. + */ + public MethodInfo getFieldGetter(FieldInfo finfo, boolean is_static) + throws CompileError + { + String fieldName = finfo.getName(); + String key = fieldName + ":getter"; + Object res = accessors.get(key); + if (res != null) + return (MethodInfo)res; // already exists. + + ClassFile cf = clazz.getClassFile(); // turn on the modified flag. + String accName = findAccessorName(cf); + try { + ConstPool cp = cf.getConstPool(); + ClassPool pool = clazz.getClassPool(); + String fieldType = finfo.getDescriptor(); + String accDesc; + if (is_static) + accDesc = "()" + fieldType; + else + accDesc = "(" + Descriptor.of(clazz) + ")" + fieldType; + + MethodInfo minfo = new MethodInfo(cp, accName, accDesc); + minfo.setAccessFlags(AccessFlag.STATIC); + minfo.addAttribute(new SyntheticAttribute(cp)); + Bytecode code = new Bytecode(cp); + if (is_static) { + code.addGetstatic(Bytecode.THIS, fieldName, fieldType); + } + else { + code.addAload(0); + code.addGetfield(Bytecode.THIS, fieldName, fieldType); + code.setMaxLocals(1); + } + + code.addReturn(Descriptor.toCtClass(fieldType, pool)); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + accessors.put(key, minfo); + return minfo; + } + catch (CannotCompileException e) { + throw new CompileError(e); + } + catch (NotFoundException e) { + throw new CompileError(e); + } + } + + /** + * Returns the method_info representing the added setter. + */ + public MethodInfo getFieldSetter(FieldInfo finfo, boolean is_static) + throws CompileError + { + String fieldName = finfo.getName(); + String key = fieldName + ":setter"; + Object res = accessors.get(key); + if (res != null) + return (MethodInfo)res; // already exists. + + ClassFile cf = clazz.getClassFile(); // turn on the modified flag. + String accName = findAccessorName(cf); + try { + ConstPool cp = cf.getConstPool(); + ClassPool pool = clazz.getClassPool(); + String fieldType = finfo.getDescriptor(); + String accDesc; + if (is_static) + accDesc = "(" + fieldType + ")V"; + else + accDesc = "(" + Descriptor.of(clazz) + fieldType + ")V"; + + MethodInfo minfo = new MethodInfo(cp, accName, accDesc); + minfo.setAccessFlags(AccessFlag.STATIC); + minfo.addAttribute(new SyntheticAttribute(cp)); + Bytecode code = new Bytecode(cp); + int reg; + if (is_static) { + reg = code.addLoad(0, Descriptor.toCtClass(fieldType, pool)); + code.addPutstatic(Bytecode.THIS, fieldName, fieldType); + } + else { + code.addAload(0); + reg = code.addLoad(1, Descriptor.toCtClass(fieldType, pool)) + + 1; + code.addPutfield(Bytecode.THIS, fieldName, fieldType); + } + + code.addReturn(null); + code.setMaxLocals(reg); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + accessors.put(key, minfo); + return minfo; + } + catch (CannotCompileException e) { + throw new CompileError(e); + } + catch (NotFoundException e) { + throw new CompileError(e); + } + } + + private String findAccessorName(ClassFile cf) { + String accName; + do { + accName = "access$" + uniqueNumber++; + } while (cf.getMethod(accName) != null); + return accName; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/CodeGen.java b/src/main/java/com/wenshuo/agent/javassist/compiler/CodeGen.java new file mode 100644 index 0000000..3345abc --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/CodeGen.java @@ -0,0 +1,1955 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import java.util.ArrayList; +import java.util.Arrays; +import com.wenshuo.agent.javassist.compiler.ast.*; +import com.wenshuo.agent.javassist.bytecode.*; + +/* The code generator is implemeted by three files: + * CodeGen.java, MemberCodeGen.java, and JvstCodeGen. + * I just wanted to split a big file into three smaller ones. + */ + +public abstract class CodeGen extends Visitor implements Opcode, TokenId { + static final String javaLangObject = "java.lang.Object"; + static final String jvmJavaLangObject = "java/lang/Object"; + + static final String javaLangString = "java.lang.String"; + static final String jvmJavaLangString = "java/lang/String"; + + protected Bytecode bytecode; + private int tempVar; + TypeChecker typeChecker; + + /** + * true if the last visited node is a return statement. + */ + protected boolean hasReturned; + + /** + * Must be true if compilation is for a static method. + */ + public boolean inStaticMethod; + + protected ArrayList breakList, continueList; + + /** + * doit() in ReturnHook is called from atReturn(). + */ + protected static abstract class ReturnHook { + ReturnHook next; + + /** + * Returns true if the generated code ends with return, + * throw, or goto. + */ + protected abstract boolean doit(Bytecode b, int opcode); + + protected ReturnHook(CodeGen gen) { + next = gen.returnHooks; + gen.returnHooks = this; + } + + protected void remove(CodeGen gen) { + gen.returnHooks = next; + } + } + + protected ReturnHook returnHooks; + + /* The following fields are used by atXXX() methods + * for returning the type of the compiled expression. + */ + protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ... + protected int arrayDim; + protected String className; // JVM-internal representation + + public CodeGen(Bytecode b) { + bytecode = b; + tempVar = -1; + typeChecker = null; + hasReturned = false; + inStaticMethod = false; + breakList = null; + continueList = null; + returnHooks = null; + } + + public void setTypeChecker(TypeChecker checker) { + typeChecker = checker; + } + + protected static void fatal() throws CompileError { + throw new CompileError("fatal"); + } + + public static boolean is2word(int type, int dim) { + return dim == 0 && (type == DOUBLE || type == LONG); + } + + public int getMaxLocals() { return bytecode.getMaxLocals(); } + + public void setMaxLocals(int n) { + bytecode.setMaxLocals(n); + } + + protected void incMaxLocals(int size) { + bytecode.incMaxLocals(size); + } + + /** + * Returns a local variable that single or double words can be + * stored in. + */ + protected int getTempVar() { + if (tempVar < 0) { + tempVar = getMaxLocals(); + incMaxLocals(2); + } + + return tempVar; + } + + protected int getLocalVar(Declarator d) { + int v = d.getLocalVar(); + if (v < 0) { + v = getMaxLocals(); // delayed variable allocation. + d.setLocalVar(v); + incMaxLocals(1); + } + + return v; + } + + /** + * Returns the JVM-internal representation of this class name. + */ + protected abstract String getThisName(); + + /** + * Returns the JVM-internal representation of this super class name. + */ + protected abstract String getSuperName() throws CompileError; + + /* Converts a class name into a JVM-internal representation. + * + * It may also expand a simple class name to java.lang.*. + * For example, this converts Object into java/lang/Object. + */ + protected abstract String resolveClassName(ASTList name) + throws CompileError; + + /* Expands a simple class name to java.lang.*. + * For example, this converts Object into java/lang/Object. + */ + protected abstract String resolveClassName(String jvmClassName) + throws CompileError; + + /** + * @param name the JVM-internal representation. + * name is not exapnded to java.lang.*. + */ + protected static String toJvmArrayName(String name, int dim) { + if (name == null) + return null; + + if (dim == 0) + return name; + else { + StringBuffer sbuf = new StringBuffer(); + int d = dim; + while (d-- > 0) + sbuf.append('['); + + sbuf.append('L'); + sbuf.append(name); + sbuf.append(';'); + + return sbuf.toString(); + } + } + + protected static String toJvmTypeName(int type, int dim) { + char c = 'I'; + switch(type) { + case BOOLEAN : + c = 'Z'; + break; + case BYTE : + c = 'B'; + break; + case CHAR : + c = 'C'; + break; + case SHORT : + c = 'S'; + break; + case INT : + c = 'I'; + break; + case LONG : + c = 'J'; + break; + case FLOAT : + c = 'F'; + break; + case DOUBLE : + c = 'D'; + break; + case VOID : + c = 'V'; + break; + } + + StringBuffer sbuf = new StringBuffer(); + while (dim-- > 0) + sbuf.append('['); + + sbuf.append(c); + return sbuf.toString(); + } + + public void compileExpr(ASTree expr) throws CompileError { + doTypeCheck(expr); + expr.accept(this); + } + + public boolean compileBooleanExpr(boolean branchIf, ASTree expr) + throws CompileError + { + doTypeCheck(expr); + return booleanExpr(branchIf, expr); + } + + public void doTypeCheck(ASTree expr) throws CompileError { + if (typeChecker != null) + expr.accept(typeChecker); + } + + public void atASTList(ASTList n) throws CompileError { fatal(); } + + public void atPair(Pair n) throws CompileError { fatal(); } + + public void atSymbol(Symbol n) throws CompileError { fatal(); } + + public void atFieldDecl(FieldDecl field) throws CompileError { + field.getInit().accept(this); + } + + public void atMethodDecl(MethodDecl method) throws CompileError { + ASTList mods = method.getModifiers(); + setMaxLocals(1); + while (mods != null) { + Keyword k = (Keyword)mods.head(); + mods = mods.tail(); + if (k.get() == STATIC) { + setMaxLocals(0); + inStaticMethod = true; + } + } + + ASTList params = method.getParams(); + while (params != null) { + atDeclarator((Declarator)params.head()); + params = params.tail(); + } + + Stmnt s = method.getBody(); + atMethodBody(s, method.isConstructor(), + method.getReturn().getType() == VOID); + } + + /** + * @param isCons true if super() must be called. + * false if the method is a class initializer. + */ + public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid) + throws CompileError + { + if (s == null) + return; + + if (isCons && needsSuperCall(s)) + insertDefaultSuperCall(); + + hasReturned = false; + s.accept(this); + if (!hasReturned) + if (isVoid) { + bytecode.addOpcode(Opcode.RETURN); + hasReturned = true; + } + else + throw new CompileError("no return statement"); + } + + private boolean needsSuperCall(Stmnt body) throws CompileError { + if (body.getOperator() == BLOCK) + body = (Stmnt)body.head(); + + if (body != null && body.getOperator() == EXPR) { + ASTree expr = body.head(); + if (expr != null && expr instanceof Expr + && ((Expr)expr).getOperator() == CALL) { + ASTree target = ((Expr)expr).head(); + if (target instanceof Keyword) { + int token = ((Keyword)target).get(); + return token != THIS && token != SUPER; + } + } + } + + return true; + } + + protected abstract void insertDefaultSuperCall() throws CompileError; + + public void atStmnt(Stmnt st) throws CompileError { + if (st == null) + return; // empty + + int op = st.getOperator(); + if (op == EXPR) { + ASTree expr = st.getLeft(); + doTypeCheck(expr); + if (expr instanceof AssignExpr) + atAssignExpr((AssignExpr)expr, false); + else if (isPlusPlusExpr(expr)) { + Expr e = (Expr)expr; + atPlusPlus(e.getOperator(), e.oprand1(), e, false); + } + else { + expr.accept(this); + if (is2word(exprType, arrayDim)) + bytecode.addOpcode(POP2); + else if (exprType != VOID) + bytecode.addOpcode(POP); + } + } + else if (op == DECL || op == BLOCK) { + ASTList list = st; + while (list != null) { + ASTree h = list.head(); + list = list.tail(); + if (h != null) + h.accept(this); + } + } + else if (op == IF) + atIfStmnt(st); + else if (op == WHILE || op == DO) + atWhileStmnt(st, op == WHILE); + else if (op == FOR) + atForStmnt(st); + else if (op == BREAK || op == CONTINUE) + atBreakStmnt(st, op == BREAK); + else if (op == TokenId.RETURN) + atReturnStmnt(st); + else if (op == THROW) + atThrowStmnt(st); + else if (op == TRY) + atTryStmnt(st); + else if (op == SWITCH) + atSwitchStmnt(st); + else if (op == SYNCHRONIZED) + atSyncStmnt(st); + else { + // LABEL, SWITCH label stament might be null?. + hasReturned = false; + throw new CompileError( + "sorry, not supported statement: TokenId " + op); + } + } + + private void atIfStmnt(Stmnt st) throws CompileError { + ASTree expr = st.head(); + Stmnt thenp = (Stmnt)st.tail().head(); + Stmnt elsep = (Stmnt)st.tail().tail().head(); + if (compileBooleanExpr(false, expr)) { + hasReturned = false; + if (elsep != null) + elsep.accept(this); + + return; + } + + int pc = bytecode.currentPc(); + int pc2 = 0; + bytecode.addIndex(0); // correct later + + hasReturned = false; + if (thenp != null) + thenp.accept(this); + + boolean thenHasReturned = hasReturned; + hasReturned = false; + + if (elsep != null && !thenHasReturned) { + bytecode.addOpcode(Opcode.GOTO); + pc2 = bytecode.currentPc(); + bytecode.addIndex(0); + } + + bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); + if (elsep != null) { + elsep.accept(this); + if (!thenHasReturned) + bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); + + hasReturned = thenHasReturned && hasReturned; + } + } + + private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError { + ArrayList prevBreakList = breakList; + ArrayList prevContList = continueList; + breakList = new ArrayList(); + continueList = new ArrayList(); + + ASTree expr = st.head(); + Stmnt body = (Stmnt)st.tail(); + + int pc = 0; + if (notDo) { + bytecode.addOpcode(Opcode.GOTO); + pc = bytecode.currentPc(); + bytecode.addIndex(0); + } + + int pc2 = bytecode.currentPc(); + if (body != null) + body.accept(this); + + int pc3 = bytecode.currentPc(); + if (notDo) + bytecode.write16bit(pc, pc3 - pc + 1); + + boolean alwaysBranch = compileBooleanExpr(true, expr); + if (alwaysBranch) { + bytecode.addOpcode(Opcode.GOTO); + alwaysBranch = breakList.size() == 0; + } + + bytecode.addIndex(pc2 - bytecode.currentPc() + 1); + patchGoto(breakList, bytecode.currentPc()); + patchGoto(continueList, pc3); + continueList = prevContList; + breakList = prevBreakList; + hasReturned = alwaysBranch; + } + + protected void patchGoto(ArrayList list, int targetPc) { + int n = list.size(); + for (int i = 0; i < n; ++i) { + int pc = ((Integer)list.get(i)).intValue(); + bytecode.write16bit(pc, targetPc - pc + 1); + } + } + + private void atForStmnt(Stmnt st) throws CompileError { + ArrayList prevBreakList = breakList; + ArrayList prevContList = continueList; + breakList = new ArrayList(); + continueList = new ArrayList(); + + Stmnt init = (Stmnt)st.head(); + ASTList p = st.tail(); + ASTree expr = p.head(); + p = p.tail(); + Stmnt update = (Stmnt)p.head(); + Stmnt body = (Stmnt)p.tail(); + + if (init != null) + init.accept(this); + + int pc = bytecode.currentPc(); + int pc2 = 0; + if (expr != null) { + if (compileBooleanExpr(false, expr)) { + // in case of "for (...; false; ...)" + continueList = prevContList; + breakList = prevBreakList; + hasReturned = false; + return; + } + + pc2 = bytecode.currentPc(); + bytecode.addIndex(0); + } + + if (body != null) + body.accept(this); + + int pc3 = bytecode.currentPc(); + if (update != null) + update.accept(this); + + bytecode.addOpcode(Opcode.GOTO); + bytecode.addIndex(pc - bytecode.currentPc() + 1); + + int pc4 = bytecode.currentPc(); + if (expr != null) + bytecode.write16bit(pc2, pc4 - pc2 + 1); + + patchGoto(breakList, pc4); + patchGoto(continueList, pc3); + continueList = prevContList; + breakList = prevBreakList; + hasReturned = false; + } + + private void atSwitchStmnt(Stmnt st) throws CompileError { + compileExpr(st.head()); + + ArrayList prevBreakList = breakList; + breakList = new ArrayList(); + int opcodePc = bytecode.currentPc(); + bytecode.addOpcode(LOOKUPSWITCH); + int npads = 3 - (opcodePc & 3); + while (npads-- > 0) + bytecode.add(0); + + Stmnt body = (Stmnt)st.tail(); + int npairs = 0; + for (ASTList list = body; list != null; list = list.tail()) + if (((Stmnt)list.head()).getOperator() == CASE) + ++npairs; + + // opcodePc2 is the position at which the default jump offset is. + int opcodePc2 = bytecode.currentPc(); + bytecode.addGap(4); + bytecode.add32bit(npairs); + bytecode.addGap(npairs * 8); + + long[] pairs = new long[npairs]; + int ipairs = 0; + int defaultPc = -1; + for (ASTList list = body; list != null; list = list.tail()) { + Stmnt label = (Stmnt)list.head(); + int op = label.getOperator(); + if (op == DEFAULT) + defaultPc = bytecode.currentPc(); + else if (op != CASE) + fatal(); + else { + pairs[ipairs++] + = ((long)computeLabel(label.head()) << 32) + + ((long)(bytecode.currentPc() - opcodePc) & 0xffffffff); + } + + hasReturned = false; + ((Stmnt)label.tail()).accept(this); + } + + Arrays.sort(pairs); + int pc = opcodePc2 + 8; + for (int i = 0; i < npairs; ++i) { + bytecode.write32bit(pc, (int)(pairs[i] >>> 32)); + bytecode.write32bit(pc + 4, (int)pairs[i]); + pc += 8; + } + + if (defaultPc < 0 || breakList.size() > 0) + hasReturned = false; + + int endPc = bytecode.currentPc(); + if (defaultPc < 0) + defaultPc = endPc; + + bytecode.write32bit(opcodePc2, defaultPc - opcodePc); + + patchGoto(breakList, endPc); + breakList = prevBreakList; + } + + private int computeLabel(ASTree expr) throws CompileError { + doTypeCheck(expr); + expr = TypeChecker.stripPlusExpr(expr); + if (expr instanceof IntConst) + return (int)((IntConst)expr).get(); + else + throw new CompileError("bad case label"); + } + + private void atBreakStmnt(Stmnt st, boolean notCont) + throws CompileError + { + if (st.head() != null) + throw new CompileError( + "sorry, not support labeled break or continue"); + + bytecode.addOpcode(Opcode.GOTO); + Integer pc = new Integer(bytecode.currentPc()); + bytecode.addIndex(0); + if (notCont) + breakList.add(pc); + else + continueList.add(pc); + } + + protected void atReturnStmnt(Stmnt st) throws CompileError { + atReturnStmnt2(st.getLeft()); + } + + protected final void atReturnStmnt2(ASTree result) throws CompileError { + int op; + if (result == null) + op = Opcode.RETURN; + else { + compileExpr(result); + if (arrayDim > 0) + op = ARETURN; + else { + int type = exprType; + if (type == DOUBLE) + op = DRETURN; + else if (type == FLOAT) + op = FRETURN; + else if (type == LONG) + op = LRETURN; + else if (isRefType(type)) + op = ARETURN; + else + op = IRETURN; + } + } + + for (ReturnHook har = returnHooks; har != null; har = har.next) + if (har.doit(bytecode, op)) { + hasReturned = true; + return; + } + + bytecode.addOpcode(op); + hasReturned = true; + } + + private void atThrowStmnt(Stmnt st) throws CompileError { + ASTree e = st.getLeft(); + compileExpr(e); + if (exprType != CLASS || arrayDim > 0) + throw new CompileError("bad throw statement"); + + bytecode.addOpcode(ATHROW); + hasReturned = true; + } + + /* overridden in MemberCodeGen + */ + protected void atTryStmnt(Stmnt st) throws CompileError { + hasReturned = false; + } + + private void atSyncStmnt(Stmnt st) throws CompileError { + int nbreaks = getListSize(breakList); + int ncontinues = getListSize(continueList); + + compileExpr(st.head()); + if (exprType != CLASS && arrayDim == 0) + throw new CompileError("bad type expr for synchronized block"); + + Bytecode bc = bytecode; + final int var = bc.getMaxLocals(); + bc.incMaxLocals(1); + bc.addOpcode(DUP); + bc.addAstore(var); + bc.addOpcode(MONITORENTER); + + ReturnHook rh = new ReturnHook(this) { + protected boolean doit(Bytecode b, int opcode) { + b.addAload(var); + b.addOpcode(MONITOREXIT); + return false; + } + }; + + int pc = bc.currentPc(); + Stmnt body = (Stmnt)st.tail(); + if (body != null) + body.accept(this); + + int pc2 = bc.currentPc(); + int pc3 = 0; + if (!hasReturned) { + rh.doit(bc, 0); // the 2nd arg is ignored. + bc.addOpcode(Opcode.GOTO); + pc3 = bc.currentPc(); + bc.addIndex(0); + } + + if (pc < pc2) { // if the body is not empty + int pc4 = bc.currentPc(); + rh.doit(bc, 0); // the 2nd arg is ignored. + bc.addOpcode(ATHROW); + bc.addExceptionHandler(pc, pc2, pc4, 0); + } + + if (!hasReturned) + bc.write16bit(pc3, bc.currentPc() - pc3 + 1); + + rh.remove(this); + + if (getListSize(breakList) != nbreaks + || getListSize(continueList) != ncontinues) + throw new CompileError( + "sorry, cannot break/continue in synchronized block"); + } + + private static int getListSize(ArrayList list) { + return list == null ? 0 : list.size(); + } + + private static boolean isPlusPlusExpr(ASTree expr) { + if (expr instanceof Expr) { + int op = ((Expr)expr).getOperator(); + return op == PLUSPLUS || op == MINUSMINUS; + } + + return false; + } + + public void atDeclarator(Declarator d) throws CompileError { + d.setLocalVar(getMaxLocals()); + d.setClassName(resolveClassName(d.getClassName())); + + int size; + if (is2word(d.getType(), d.getArrayDim())) + size = 2; + else + size = 1; + + incMaxLocals(size); + + /* NOTE: Array initializers has not been supported. + */ + ASTree init = d.getInitializer(); + if (init != null) { + doTypeCheck(init); + atVariableAssign(null, '=', null, d, init, false); + } + } + + public abstract void atNewExpr(NewExpr n) throws CompileError; + + public abstract void atArrayInit(ArrayInit init) throws CompileError; + + public void atAssignExpr(AssignExpr expr) throws CompileError { + atAssignExpr(expr, true); + } + + protected void atAssignExpr(AssignExpr expr, boolean doDup) + throws CompileError + { + // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>= + int op = expr.getOperator(); + ASTree left = expr.oprand1(); + ASTree right = expr.oprand2(); + if (left instanceof Variable) + atVariableAssign(expr, op, (Variable)left, + ((Variable)left).getDeclarator(), + right, doDup); + else { + if (left instanceof Expr) { + Expr e = (Expr)left; + if (e.getOperator() == ARRAY) { + atArrayAssign(expr, op, (Expr)left, right, doDup); + return; + } + } + + atFieldAssign(expr, op, left, right, doDup); + } + } + + protected static void badAssign(Expr expr) throws CompileError { + String msg; + if (expr == null) + msg = "incompatible type for assignment"; + else + msg = "incompatible type for " + expr.getName(); + + throw new CompileError(msg); + } + + /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. + * + * expr and var can be null. + */ + private void atVariableAssign(Expr expr, int op, Variable var, + Declarator d, ASTree right, + boolean doDup) throws CompileError + { + int varType = d.getType(); + int varArray = d.getArrayDim(); + String varClass = d.getClassName(); + int varNo = getLocalVar(d); + + if (op != '=') + atVariable(var); + + // expr is null if the caller is atDeclarator(). + if (expr == null && right instanceof ArrayInit) + atArrayVariableAssign((ArrayInit)right, varType, varArray, varClass); + else + atAssignCore(expr, op, right, varType, varArray, varClass); + + if (doDup) + if (is2word(varType, varArray)) + bytecode.addOpcode(DUP2); + else + bytecode.addOpcode(DUP); + + if (varArray > 0) + bytecode.addAstore(varNo); + else if (varType == DOUBLE) + bytecode.addDstore(varNo); + else if (varType == FLOAT) + bytecode.addFstore(varNo); + else if (varType == LONG) + bytecode.addLstore(varNo); + else if (isRefType(varType)) + bytecode.addAstore(varNo); + else + bytecode.addIstore(varNo); + + exprType = varType; + arrayDim = varArray; + className = varClass; + } + + protected abstract void atArrayVariableAssign(ArrayInit init, + int varType, int varArray, String varClass) throws CompileError; + + private void atArrayAssign(Expr expr, int op, Expr array, + ASTree right, boolean doDup) throws CompileError + { + arrayAccess(array.oprand1(), array.oprand2()); + + if (op != '=') { + bytecode.addOpcode(DUP2); + bytecode.addOpcode(getArrayReadOp(exprType, arrayDim)); + } + + int aType = exprType; + int aDim = arrayDim; + String cname = className; + + atAssignCore(expr, op, right, aType, aDim, cname); + + if (doDup) + if (is2word(aType, aDim)) + bytecode.addOpcode(DUP2_X2); + else + bytecode.addOpcode(DUP_X2); + + bytecode.addOpcode(getArrayWriteOp(aType, aDim)); + exprType = aType; + arrayDim = aDim; + className = cname; + } + + protected abstract void atFieldAssign(Expr expr, int op, ASTree left, + ASTree right, boolean doDup) throws CompileError; + + protected void atAssignCore(Expr expr, int op, ASTree right, + int type, int dim, String cname) + throws CompileError + { + if (op == PLUS_E && dim == 0 && type == CLASS) + atStringPlusEq(expr, type, dim, cname, right); + else { + right.accept(this); + if (invalidDim(exprType, arrayDim, className, type, dim, cname, + false) || (op != '=' && dim > 0)) + badAssign(expr); + + if (op != '=') { + int token = assignOps[op - MOD_E]; + int k = lookupBinOp(token); + if (k < 0) + fatal(); + + atArithBinExpr(expr, token, k, type); + } + } + + if (op != '=' || (dim == 0 && !isRefType(type))) + atNumCastExpr(exprType, type); + + // type check should be done here. + } + + private void atStringPlusEq(Expr expr, int type, int dim, String cname, + ASTree right) + throws CompileError + { + if (!jvmJavaLangString.equals(cname)) + badAssign(expr); + + convToString(type, dim); // the value might be null. + right.accept(this); + convToString(exprType, arrayDim); + bytecode.addInvokevirtual(javaLangString, "concat", + "(Ljava/lang/String;)Ljava/lang/String;"); + exprType = CLASS; + arrayDim = 0; + className = jvmJavaLangString; + } + + private boolean invalidDim(int srcType, int srcDim, String srcClass, + int destType, int destDim, String destClass, + boolean isCast) + { + if (srcDim != destDim) + if (srcType == NULL) + return false; + else if (destDim == 0 && destType == CLASS + && jvmJavaLangObject.equals(destClass)) + return false; + else if (isCast && srcDim == 0 && srcType == CLASS + && jvmJavaLangObject.equals(srcClass)) + return false; + else + return true; + + return false; + } + + public void atCondExpr(CondExpr expr) throws CompileError { + if (booleanExpr(false, expr.condExpr())) + expr.elseExpr().accept(this); + else { + int pc = bytecode.currentPc(); + bytecode.addIndex(0); // correct later + expr.thenExpr().accept(this); + int dim1 = arrayDim; + bytecode.addOpcode(Opcode.GOTO); + int pc2 = bytecode.currentPc(); + bytecode.addIndex(0); + bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); + expr.elseExpr().accept(this); + if (dim1 != arrayDim) + throw new CompileError("type mismatch in ?:"); + + bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); + } + } + + static final int[] binOp = { + '+', DADD, FADD, LADD, IADD, + '-', DSUB, FSUB, LSUB, ISUB, + '*', DMUL, FMUL, LMUL, IMUL, + '/', DDIV, FDIV, LDIV, IDIV, + '%', DREM, FREM, LREM, IREM, + '|', NOP, NOP, LOR, IOR, + '^', NOP, NOP, LXOR, IXOR, + '&', NOP, NOP, LAND, IAND, + LSHIFT, NOP, NOP, LSHL, ISHL, + RSHIFT, NOP, NOP, LSHR, ISHR, + ARSHIFT, NOP, NOP, LUSHR, IUSHR }; + + static int lookupBinOp(int token) { + int[] code = binOp; + int s = code.length; + for (int k = 0; k < s; k = k + 5) + if (code[k] == token) + return k; + + return -1; + } + + public void atBinExpr(BinExpr expr) throws CompileError { + int token = expr.getOperator(); + + /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>> + */ + int k = lookupBinOp(token); + if (k >= 0) { + expr.oprand1().accept(this); + ASTree right = expr.oprand2(); + if (right == null) + return; // see TypeChecker.atBinExpr(). + + int type1 = exprType; + int dim1 = arrayDim; + String cname1 = className; + right.accept(this); + if (dim1 != arrayDim) + throw new CompileError("incompatible array types"); + + if (token == '+' && dim1 == 0 + && (type1 == CLASS || exprType == CLASS)) + atStringConcatExpr(expr, type1, dim1, cname1); + else + atArithBinExpr(expr, token, k, type1); + } + else { + /* equation: &&, ||, ==, !=, <=, >=, <, > + */ + if (!booleanExpr(true, expr)) { + bytecode.addIndex(7); + bytecode.addIconst(0); // false + bytecode.addOpcode(Opcode.GOTO); + bytecode.addIndex(4); + } + + bytecode.addIconst(1); // true + } + } + + /* arrayDim values of the two oprands must be equal. + * If an oprand type is not a numeric type, this method + * throws an exception. + */ + private void atArithBinExpr(Expr expr, int token, + int index, int type1) throws CompileError + { + if (arrayDim != 0) + badTypes(expr); + + int type2 = exprType; + if (token == LSHIFT || token == RSHIFT || token == ARSHIFT) + if (type2 == INT || type2 == SHORT + || type2 == CHAR || type2 == BYTE) + exprType = type1; + else + badTypes(expr); + else + convertOprandTypes(type1, type2, expr); + + int p = typePrecedence(exprType); + if (p >= 0) { + int op = binOp[index + p + 1]; + if (op != NOP) { + if (p == P_INT && exprType != BOOLEAN) + exprType = INT; // type1 may be BYTE, ... + + bytecode.addOpcode(op); + return; + } + } + + badTypes(expr); + } + + private void atStringConcatExpr(Expr expr, int type1, int dim1, + String cname1) throws CompileError + { + int type2 = exprType; + int dim2 = arrayDim; + boolean type2Is2 = is2word(type2, dim2); + boolean type2IsString + = (type2 == CLASS && jvmJavaLangString.equals(className)); + + if (type2Is2) + convToString(type2, dim2); + + if (is2word(type1, dim1)) { + bytecode.addOpcode(DUP_X2); + bytecode.addOpcode(POP); + } + else + bytecode.addOpcode(SWAP); + + // even if type1 is String, the left operand might be null. + convToString(type1, dim1); + bytecode.addOpcode(SWAP); + + if (!type2Is2 && !type2IsString) + convToString(type2, dim2); + + bytecode.addInvokevirtual(javaLangString, "concat", + "(Ljava/lang/String;)Ljava/lang/String;"); + exprType = CLASS; + arrayDim = 0; + className = jvmJavaLangString; + } + + private void convToString(int type, int dim) throws CompileError { + final String method = "valueOf"; + + if (isRefType(type) || dim > 0) + bytecode.addInvokestatic(javaLangString, method, + "(Ljava/lang/Object;)Ljava/lang/String;"); + else if (type == DOUBLE) + bytecode.addInvokestatic(javaLangString, method, + "(D)Ljava/lang/String;"); + else if (type == FLOAT) + bytecode.addInvokestatic(javaLangString, method, + "(F)Ljava/lang/String;"); + else if (type == LONG) + bytecode.addInvokestatic(javaLangString, method, + "(J)Ljava/lang/String;"); + else if (type == BOOLEAN) + bytecode.addInvokestatic(javaLangString, method, + "(Z)Ljava/lang/String;"); + else if (type == CHAR) + bytecode.addInvokestatic(javaLangString, method, + "(C)Ljava/lang/String;"); + else if (type == VOID) + throw new CompileError("void type expression"); + else /* INT, BYTE, SHORT */ + bytecode.addInvokestatic(javaLangString, method, + "(I)Ljava/lang/String;"); + } + + /* Produces the opcode to branch if the condition is true. + * The oprand (branch offset) is not produced. + * + * @return true if the compiled code is GOTO (always branch). + * GOTO is not produced. + */ + private boolean booleanExpr(boolean branchIf, ASTree expr) + throws CompileError + { + boolean isAndAnd; + int op = getCompOperator(expr); + if (op == EQ) { // ==, !=, ... + BinExpr bexpr = (BinExpr)expr; + int type1 = compileOprands(bexpr); + // here, arrayDim might represent the array dim. of the left oprand + // if the right oprand is NULL. + compareExpr(branchIf, bexpr.getOperator(), type1, bexpr); + } + else if (op == '!') + return booleanExpr(!branchIf, ((Expr)expr).oprand1()); + else if ((isAndAnd = (op == ANDAND)) || op == OROR) { + BinExpr bexpr = (BinExpr)expr; + if (booleanExpr(!isAndAnd, bexpr.oprand1())) { + exprType = BOOLEAN; + arrayDim = 0; + return true; + } + else { + int pc = bytecode.currentPc(); + bytecode.addIndex(0); // correct later + if (booleanExpr(isAndAnd, bexpr.oprand2())) + bytecode.addOpcode(Opcode.GOTO); + + bytecode.write16bit(pc, bytecode.currentPc() - pc + 3); + if (branchIf != isAndAnd) { + bytecode.addIndex(6); // skip GOTO instruction + bytecode.addOpcode(Opcode.GOTO); + } + } + } + else if (isAlwaysBranch(expr, branchIf)) { + // Opcode.GOTO is not added here. The caller must add it. + exprType = BOOLEAN; + arrayDim = 0; + return true; // always branch + } + else { // others + expr.accept(this); + if (exprType != BOOLEAN || arrayDim != 0) + throw new CompileError("boolean expr is required"); + + bytecode.addOpcode(branchIf ? IFNE : IFEQ); + } + + exprType = BOOLEAN; + arrayDim = 0; + return false; + } + + private static boolean isAlwaysBranch(ASTree expr, boolean branchIf) { + if (expr instanceof Keyword) { + int t = ((Keyword)expr).get(); + return branchIf ? t == TRUE : t == FALSE; + } + + return false; + } + + static int getCompOperator(ASTree expr) throws CompileError { + if (expr instanceof Expr) { + Expr bexpr = (Expr)expr; + int token = bexpr.getOperator(); + if (token == '!') + return '!'; + else if ((bexpr instanceof BinExpr) + && token != OROR && token != ANDAND + && token != '&' && token != '|') + return EQ; // ==, !=, ... + else + return token; + } + + return ' '; // others + } + + private int compileOprands(BinExpr expr) throws CompileError { + expr.oprand1().accept(this); + int type1 = exprType; + int dim1 = arrayDim; + expr.oprand2().accept(this); + if (dim1 != arrayDim) + if (type1 != NULL && exprType != NULL) + throw new CompileError("incompatible array types"); + else if (exprType == NULL) + arrayDim = dim1; + + if (type1 == NULL) + return exprType; + else + return type1; + } + + private static final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE, + NEQ, IF_ICMPNE, IF_ICMPEQ, + LE, IF_ICMPLE, IF_ICMPGT, + GE, IF_ICMPGE, IF_ICMPLT, + '<', IF_ICMPLT, IF_ICMPGE, + '>', IF_ICMPGT, IF_ICMPLE }; + + private static final int ifOp2[] = { EQ, IFEQ, IFNE, + NEQ, IFNE, IFEQ, + LE, IFLE, IFGT, + GE, IFGE, IFLT, + '<', IFLT, IFGE, + '>', IFGT, IFLE }; + + /* Produces the opcode to branch if the condition is true. + * The oprands are not produced. + * + * Parameter expr - compare expression ==, !=, <=, >=, <, > + */ + private void compareExpr(boolean branchIf, + int token, int type1, BinExpr expr) + throws CompileError + { + if (arrayDim == 0) + convertOprandTypes(type1, exprType, expr); + + int p = typePrecedence(exprType); + if (p == P_OTHER || arrayDim > 0) + if (token == EQ) + bytecode.addOpcode(branchIf ? IF_ACMPEQ : IF_ACMPNE); + else if (token == NEQ) + bytecode.addOpcode(branchIf ? IF_ACMPNE : IF_ACMPEQ); + else + badTypes(expr); + else + if (p == P_INT) { + int op[] = ifOp; + for (int i = 0; i < op.length; i += 3) + if (op[i] == token) { + bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]); + return; + } + + badTypes(expr); + } + else { + if (p == P_DOUBLE) + if (token == '<' || token == LE) + bytecode.addOpcode(DCMPG); + else + bytecode.addOpcode(DCMPL); + else if (p == P_FLOAT) + if (token == '<' || token == LE) + bytecode.addOpcode(FCMPG); + else + bytecode.addOpcode(FCMPL); + else if (p == P_LONG) + bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: < + else + fatal(); + + int[] op = ifOp2; + for (int i = 0; i < op.length; i += 3) + if (op[i] == token) { + bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]); + return; + } + + badTypes(expr); + } + } + + protected static void badTypes(Expr expr) throws CompileError { + throw new CompileError("invalid types for " + expr.getName()); + } + + private static final int P_DOUBLE = 0; + private static final int P_FLOAT = 1; + private static final int P_LONG = 2; + private static final int P_INT = 3; + private static final int P_OTHER = -1; + + protected static boolean isRefType(int type) { + return type == CLASS || type == NULL; + } + + private static int typePrecedence(int type) { + if (type == DOUBLE) + return P_DOUBLE; + else if (type == FLOAT) + return P_FLOAT; + else if (type == LONG) + return P_LONG; + else if (isRefType(type)) + return P_OTHER; + else if (type == VOID) + return P_OTHER; // this is wrong, but ... + else + return P_INT; // BOOLEAN, BYTE, CHAR, SHORT, INT + } + + // used in TypeChecker. + static boolean isP_INT(int type) { + return typePrecedence(type) == P_INT; + } + + // used in TypeChecker. + static boolean rightIsStrong(int type1, int type2) { + int type1_p = typePrecedence(type1); + int type2_p = typePrecedence(type2); + return type1_p >= 0 && type2_p >= 0 && type1_p > type2_p; + } + + private static final int[] castOp = { + /* D F L I */ + /* double */ NOP, D2F, D2L, D2I, + /* float */ F2D, NOP, F2L, F2I, + /* long */ L2D, L2F, NOP, L2I, + /* other */ I2D, I2F, I2L, NOP }; + + /* do implicit type conversion. + * arrayDim values of the two oprands must be zero. + */ + private void convertOprandTypes(int type1, int type2, Expr expr) + throws CompileError + { + boolean rightStrong; + int type1_p = typePrecedence(type1); + int type2_p = typePrecedence(type2); + + if (type2_p < 0 && type1_p < 0) // not primitive types + return; + + if (type2_p < 0 || type1_p < 0) // either is not a primitive type + badTypes(expr); + + int op, result_type; + if (type1_p <= type2_p) { + rightStrong = false; + exprType = type1; + op = castOp[type2_p * 4 + type1_p]; + result_type = type1_p; + } + else { + rightStrong = true; + op = castOp[type1_p * 4 + type2_p]; + result_type = type2_p; + } + + if (rightStrong) { + if (result_type == P_DOUBLE || result_type == P_LONG) { + if (type1_p == P_DOUBLE || type1_p == P_LONG) + bytecode.addOpcode(DUP2_X2); + else + bytecode.addOpcode(DUP2_X1); + + bytecode.addOpcode(POP2); + bytecode.addOpcode(op); + bytecode.addOpcode(DUP2_X2); + bytecode.addOpcode(POP2); + } + else if (result_type == P_FLOAT) { + if (type1_p == P_LONG) { + bytecode.addOpcode(DUP_X2); + bytecode.addOpcode(POP); + } + else + bytecode.addOpcode(SWAP); + + bytecode.addOpcode(op); + bytecode.addOpcode(SWAP); + } + else + fatal(); + } + else if (op != NOP) + bytecode.addOpcode(op); + } + + public void atCastExpr(CastExpr expr) throws CompileError { + String cname = resolveClassName(expr.getClassName()); + String toClass = checkCastExpr(expr, cname); + int srcType = exprType; + exprType = expr.getType(); + arrayDim = expr.getArrayDim(); + className = cname; + if (toClass == null) + atNumCastExpr(srcType, exprType); // built-in type + else + bytecode.addCheckcast(toClass); + } + + public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { + String cname = resolveClassName(expr.getClassName()); + String toClass = checkCastExpr(expr, cname); + bytecode.addInstanceof(toClass); + exprType = BOOLEAN; + arrayDim = 0; + } + + private String checkCastExpr(CastExpr expr, String name) + throws CompileError + { + final String msg = "invalid cast"; + ASTree oprand = expr.getOprand(); + int dim = expr.getArrayDim(); + int type = expr.getType(); + oprand.accept(this); + int srcType = exprType; + if (invalidDim(srcType, arrayDim, className, type, dim, name, true) + || srcType == VOID || type == VOID) + throw new CompileError(msg); + + if (type == CLASS) { + if (!isRefType(srcType)) + throw new CompileError(msg); + + return toJvmArrayName(name, dim); + } + else + if (dim > 0) + return toJvmTypeName(type, dim); + else + return null; // built-in type + } + + void atNumCastExpr(int srcType, int destType) + throws CompileError + { + if (srcType == destType) + return; + + int op, op2; + int stype = typePrecedence(srcType); + int dtype = typePrecedence(destType); + if (0 <= stype && stype < 3) + op = castOp[stype * 4 + dtype]; + else + op = NOP; + + if (destType == DOUBLE) + op2 = I2D; + else if (destType == FLOAT) + op2 = I2F; + else if (destType == LONG) + op2 = I2L; + else if (destType == SHORT) + op2 = I2S; + else if (destType == CHAR) + op2 = I2C; + else if (destType == BYTE) + op2 = I2B; + else + op2 = NOP; + + if (op != NOP) + bytecode.addOpcode(op); + + if (op == NOP || op == L2I || op == F2I || op == D2I) + if (op2 != NOP) + bytecode.addOpcode(op2); + } + + public void atExpr(Expr expr) throws CompileError { + // array access, member access, + // (unary) +, (unary) -, ++, --, !, ~ + + int token = expr.getOperator(); + ASTree oprand = expr.oprand1(); + if (token == '.') { + String member = ((Symbol)expr.oprand2()).get(); + if (member.equals("class")) + atClassObject(expr); // .class + else + atFieldRead(expr); + } + else if (token == MEMBER) { // field read + /* MEMBER ('#') is an extension by Javassist. + * The compiler internally uses # for compiling .class + * expressions such as "int.class". + */ + atFieldRead(expr); + } + else if (token == ARRAY) + atArrayRead(oprand, expr.oprand2()); + else if (token == PLUSPLUS || token == MINUSMINUS) + atPlusPlus(token, oprand, expr, true); + else if (token == '!') { + if (!booleanExpr(false, expr)) { + bytecode.addIndex(7); + bytecode.addIconst(1); + bytecode.addOpcode(Opcode.GOTO); + bytecode.addIndex(4); + } + + bytecode.addIconst(0); + } + else if (token == CALL) // method call + fatal(); + else { + expr.oprand1().accept(this); + int type = typePrecedence(exprType); + if (arrayDim > 0) + badType(expr); + + if (token == '-') { + if (type == P_DOUBLE) + bytecode.addOpcode(DNEG); + else if (type == P_FLOAT) + bytecode.addOpcode(FNEG); + else if (type == P_LONG) + bytecode.addOpcode(LNEG); + else if (type == P_INT) { + bytecode.addOpcode(INEG); + exprType = INT; // type may be BYTE, ... + } + else + badType(expr); + } + else if (token == '~') { + if (type == P_INT) { + bytecode.addIconst(-1); + bytecode.addOpcode(IXOR); + exprType = INT; // type may be BYTE. ... + } + else if (type == P_LONG) { + bytecode.addLconst(-1); + bytecode.addOpcode(LXOR); + } + else + badType(expr); + + } + else if (token == '+') { + if (type == P_OTHER) + badType(expr); + + // do nothing. ignore. + } + else + fatal(); + } + } + + protected static void badType(Expr expr) throws CompileError { + throw new CompileError("invalid type for " + expr.getName()); + } + + public abstract void atCallExpr(CallExpr expr) throws CompileError; + + protected abstract void atFieldRead(ASTree expr) throws CompileError; + + public void atClassObject(Expr expr) throws CompileError { + ASTree op1 = expr.oprand1(); + if (!(op1 instanceof Symbol)) + throw new CompileError("fatal error: badly parsed .class expr"); + + String cname = ((Symbol)op1).get(); + if (cname.startsWith("[")) { + int i = cname.indexOf("[L"); + if (i >= 0) { + String name = cname.substring(i + 2, cname.length() - 1); + String name2 = resolveClassName(name); + if (!name.equals(name2)) { + /* For example, to obtain String[].class, + * "[Ljava.lang.String;" (not "[Ljava/lang/String"!) + * must be passed to Class.forName(). + */ + name2 = MemberResolver.jvmToJavaName(name2); + StringBuffer sbuf = new StringBuffer(); + while (i-- >= 0) + sbuf.append('['); + + sbuf.append('L').append(name2).append(';'); + cname = sbuf.toString(); + } + } + } + else { + cname = resolveClassName(MemberResolver.javaToJvmName(cname)); + cname = MemberResolver.jvmToJavaName(cname); + } + + atClassObject2(cname); + exprType = CLASS; + arrayDim = 0; + className = "java/lang/Class"; + } + + /* MemberCodeGen overrides this method. + */ + protected void atClassObject2(String cname) throws CompileError { + int start = bytecode.currentPc(); + bytecode.addLdc(cname); + bytecode.addInvokestatic("java.lang.Class", "forName", + "(Ljava/lang/String;)Ljava/lang/Class;"); + int end = bytecode.currentPc(); + bytecode.addOpcode(Opcode.GOTO); + int pc = bytecode.currentPc(); + bytecode.addIndex(0); // correct later + + bytecode.addExceptionHandler(start, end, bytecode.currentPc(), + "java.lang.ClassNotFoundException"); + + /* -- the following code is for inlining a call to DotClass.fail(). + + int var = getMaxLocals(); + incMaxLocals(1); + bytecode.growStack(1); + bytecode.addAstore(var); + + bytecode.addNew("java.lang.NoClassDefFoundError"); + bytecode.addOpcode(DUP); + bytecode.addAload(var); + bytecode.addInvokevirtual("java.lang.ClassNotFoundException", + "getMessage", "()Ljava/lang/String;"); + bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "", + "(Ljava/lang/String;)V"); + */ + + bytecode.growStack(1); + bytecode.addInvokestatic("javassist.runtime.DotClass", "fail", + "(Ljava/lang/ClassNotFoundException;)" + + "Ljava/lang/NoClassDefFoundError;"); + bytecode.addOpcode(ATHROW); + bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); + } + + public void atArrayRead(ASTree array, ASTree index) + throws CompileError + { + arrayAccess(array, index); + bytecode.addOpcode(getArrayReadOp(exprType, arrayDim)); + } + + protected void arrayAccess(ASTree array, ASTree index) + throws CompileError + { + array.accept(this); + int type = exprType; + int dim = arrayDim; + if (dim == 0) + throw new CompileError("bad array access"); + + String cname = className; + + index.accept(this); + if (typePrecedence(exprType) != P_INT || arrayDim > 0) + throw new CompileError("bad array index"); + + exprType = type; + arrayDim = dim - 1; + className = cname; + } + + protected static int getArrayReadOp(int type, int dim) { + if (dim > 0) + return AALOAD; + + switch (type) { + case DOUBLE : + return DALOAD; + case FLOAT : + return FALOAD; + case LONG : + return LALOAD; + case INT : + return IALOAD; + case SHORT : + return SALOAD; + case CHAR : + return CALOAD; + case BYTE : + case BOOLEAN : + return BALOAD; + default : + return AALOAD; + } + } + + protected static int getArrayWriteOp(int type, int dim) { + if (dim > 0) + return AASTORE; + + switch (type) { + case DOUBLE : + return DASTORE; + case FLOAT : + return FASTORE; + case LONG : + return LASTORE; + case INT : + return IASTORE; + case SHORT : + return SASTORE; + case CHAR : + return CASTORE; + case BYTE : + case BOOLEAN : + return BASTORE; + default : + return AASTORE; + } + } + + private void atPlusPlus(int token, ASTree oprand, Expr expr, + boolean doDup) throws CompileError + { + boolean isPost = oprand == null; // ++i or i++? + if (isPost) + oprand = expr.oprand2(); + + if (oprand instanceof Variable) { + Declarator d = ((Variable)oprand).getDeclarator(); + int t = exprType = d.getType(); + arrayDim = d.getArrayDim(); + int var = getLocalVar(d); + if (arrayDim > 0) + badType(expr); + + if (t == DOUBLE) { + bytecode.addDload(var); + if (doDup && isPost) + bytecode.addOpcode(DUP2); + + bytecode.addDconst(1.0); + bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB); + if (doDup && !isPost) + bytecode.addOpcode(DUP2); + + bytecode.addDstore(var); + } + else if (t == LONG) { + bytecode.addLload(var); + if (doDup && isPost) + bytecode.addOpcode(DUP2); + + bytecode.addLconst((long)1); + bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB); + if (doDup && !isPost) + bytecode.addOpcode(DUP2); + + bytecode.addLstore(var); + } + else if (t == FLOAT) { + bytecode.addFload(var); + if (doDup && isPost) + bytecode.addOpcode(DUP); + + bytecode.addFconst(1.0f); + bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB); + if (doDup && !isPost) + bytecode.addOpcode(DUP); + + bytecode.addFstore(var); + } + else if (t == BYTE || t == CHAR || t == SHORT || t == INT) { + if (doDup && isPost) + bytecode.addIload(var); + + int delta = token == PLUSPLUS ? 1 : -1; + if (var > 0xff) { + bytecode.addOpcode(WIDE); + bytecode.addOpcode(IINC); + bytecode.addIndex(var); + bytecode.addIndex(delta); + } + else { + bytecode.addOpcode(IINC); + bytecode.add(var); + bytecode.add(delta); + } + + if (doDup && !isPost) + bytecode.addIload(var); + } + else + badType(expr); + } + else { + if (oprand instanceof Expr) { + Expr e = (Expr)oprand; + if (e.getOperator() == ARRAY) { + atArrayPlusPlus(token, isPost, e, doDup); + return; + } + } + + atFieldPlusPlus(token, isPost, oprand, expr, doDup); + } + } + + public void atArrayPlusPlus(int token, boolean isPost, + Expr expr, boolean doDup) throws CompileError + { + arrayAccess(expr.oprand1(), expr.oprand2()); + int t = exprType; + int dim = arrayDim; + if (dim > 0) + badType(expr); + + bytecode.addOpcode(DUP2); + bytecode.addOpcode(getArrayReadOp(t, arrayDim)); + int dup_code = is2word(t, dim) ? DUP2_X2 : DUP_X2; + atPlusPlusCore(dup_code, doDup, token, isPost, expr); + bytecode.addOpcode(getArrayWriteOp(t, dim)); + } + + protected void atPlusPlusCore(int dup_code, boolean doDup, + int token, boolean isPost, + Expr expr) throws CompileError + { + int t = exprType; + + if (doDup && isPost) + bytecode.addOpcode(dup_code); + + if (t == INT || t == BYTE || t == CHAR || t == SHORT) { + bytecode.addIconst(1); + bytecode.addOpcode(token == PLUSPLUS ? IADD : ISUB); + exprType = INT; + } + else if (t == LONG) { + bytecode.addLconst((long)1); + bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB); + } + else if (t == FLOAT) { + bytecode.addFconst(1.0f); + bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB); + } + else if (t == DOUBLE) { + bytecode.addDconst(1.0); + bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB); + } + else + badType(expr); + + if (doDup && !isPost) + bytecode.addOpcode(dup_code); + } + + protected abstract void atFieldPlusPlus(int token, boolean isPost, + ASTree oprand, Expr expr, boolean doDup) throws CompileError; + + public abstract void atMember(Member n) throws CompileError; + + public void atVariable(Variable v) throws CompileError { + Declarator d = v.getDeclarator(); + exprType = d.getType(); + arrayDim = d.getArrayDim(); + className = d.getClassName(); + int var = getLocalVar(d); + + if (arrayDim > 0) + bytecode.addAload(var); + else + switch (exprType) { + case CLASS : + bytecode.addAload(var); + break; + case LONG : + bytecode.addLload(var); + break; + case FLOAT : + bytecode.addFload(var); + break; + case DOUBLE : + bytecode.addDload(var); + break; + default : // BOOLEAN, BYTE, CHAR, SHORT, INT + bytecode.addIload(var); + break; + } + } + + public void atKeyword(Keyword k) throws CompileError { + arrayDim = 0; + int token = k.get(); + switch (token) { + case TRUE : + bytecode.addIconst(1); + exprType = BOOLEAN; + break; + case FALSE : + bytecode.addIconst(0); + exprType = BOOLEAN; + break; + case NULL : + bytecode.addOpcode(ACONST_NULL); + exprType = NULL; + break; + case THIS : + case SUPER : + if (inStaticMethod) + throw new CompileError("not-available: " + + (token == THIS ? "this" : "super")); + + bytecode.addAload(0); + exprType = CLASS; + if (token == THIS) + className = getThisName(); + else + className = getSuperName(); + break; + default : + fatal(); + } + } + + public void atStringL(StringL s) throws CompileError { + exprType = CLASS; + arrayDim = 0; + className = jvmJavaLangString; + bytecode.addLdc(s.get()); + } + + public void atIntConst(IntConst i) throws CompileError { + arrayDim = 0; + long value = i.get(); + int type = i.getType(); + if (type == IntConstant || type == CharConstant) { + exprType = (type == IntConstant ? INT : CHAR); + bytecode.addIconst((int)value); + } + else { + exprType = LONG; + bytecode.addLconst(value); + } + } + + public void atDoubleConst(DoubleConst d) throws CompileError { + arrayDim = 0; + if (d.getType() == DoubleConstant) { + exprType = DOUBLE; + bytecode.addDconst(d.get()); + } + else { + exprType = FLOAT; + bytecode.addFconst((float)d.get()); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/CompileError.java b/src/main/java/com/wenshuo/agent/javassist/compiler/CompileError.java new file mode 100644 index 0000000..7944620 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/CompileError.java @@ -0,0 +1,53 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.NotFoundException; + +public class CompileError extends Exception { + private Lex lex; + private String reason; + + public CompileError(String s, Lex l) { + reason = s; + lex = l; + } + + public CompileError(String s) { + reason = s; + lex = null; + } + + public CompileError(CannotCompileException e) { + this(e.getReason()); + } + + public CompileError(NotFoundException e) { + this("cannot find " + e.getMessage()); + } + + public Lex getLex() { return lex; } + + public String getMessage() { + return reason; + } + + public String toString() { + return "compile error: " + reason; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/Javac.java b/src/main/java/com/wenshuo/agent/javassist/compiler/Javac.java new file mode 100644 index 0000000..cfbeb4b --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/Javac.java @@ -0,0 +1,610 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtPrimitiveType; +import com.wenshuo.agent.javassist.CtMember; +import com.wenshuo.agent.javassist.CtField; +import com.wenshuo.agent.javassist.CtBehavior; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.CtConstructor; +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.Modifier; +import com.wenshuo.agent.javassist.bytecode.Bytecode; +import com.wenshuo.agent.javassist.bytecode.CodeAttribute; +import com.wenshuo.agent.javassist.bytecode.LocalVariableAttribute; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.Opcode; +import com.wenshuo.agent.javassist.NotFoundException; + +import com.wenshuo.agent.javassist.compiler.ast.*; + +public class Javac { + JvstCodeGen gen; + SymbolTable stable; + private Bytecode bytecode; + + public static final String param0Name = "$0"; + public static final String resultVarName = "$_"; + public static final String proceedName = "$proceed"; + + /** + * Constructs a compiler. + * + * @param thisClass the class that a compiled method/field + * belongs to. + */ + public Javac(CtClass thisClass) { + this(new Bytecode(thisClass.getClassFile2().getConstPool(), 0, 0), + thisClass); + } + + /** + * Constructs a compiler. + * The produced bytecode is stored in the Bytecode object + * specified by b. + * + * @param thisClass the class that a compiled method/field + * belongs to. + */ + public Javac(Bytecode b, CtClass thisClass) { + gen = new JvstCodeGen(b, thisClass, thisClass.getClassPool()); + stable = new SymbolTable(); + bytecode = b; + } + + /** + * Returns the produced bytecode. + */ + public Bytecode getBytecode() { return bytecode; } + + /** + * Compiles a method, constructor, or field declaration + * to a class. + * A field declaration can declare only one field. + * + *

In a method or constructor body, $0, $1, ... and $_ + * are not available. + * + * @return a CtMethod, CtConstructor, + * or CtField object. + * @see #recordProceed(String,String) + */ + public CtMember compile(String src) throws CompileError { + Parser p = new Parser(new Lex(src)); + ASTList mem = p.parseMember1(stable); + try { + if (mem instanceof FieldDecl) + return compileField((FieldDecl)mem); + else { + CtBehavior cb = compileMethod(p, (MethodDecl)mem); + CtClass decl = cb.getDeclaringClass(); + cb.getMethodInfo2() + .rebuildStackMapIf6(decl.getClassPool(), + decl.getClassFile2()); + return cb; + } + } + catch (BadBytecode bb) { + throw new CompileError(bb.getMessage()); + } + catch (CannotCompileException e) { + throw new CompileError(e.getMessage()); + } + } + + public static class CtFieldWithInit extends CtField { + private ASTree init; + + CtFieldWithInit(CtClass type, String name, CtClass declaring) + throws CannotCompileException + { + super(type, name, declaring); + init = null; + } + + protected void setInit(ASTree i) { init = i; } + + protected ASTree getInitAST() { + return init; + } + } + + private CtField compileField(FieldDecl fd) + throws CompileError, CannotCompileException + { + CtFieldWithInit f; + Declarator d = fd.getDeclarator(); + f = new CtFieldWithInit(gen.resolver.lookupClass(d), + d.getVariable().get(), gen.getThisClass()); + f.setModifiers(MemberResolver.getModifiers(fd.getModifiers())); + if (fd.getInit() != null) + f.setInit(fd.getInit()); + + return f; + } + + private CtBehavior compileMethod(Parser p, MethodDecl md) + throws CompileError + { + int mod = MemberResolver.getModifiers(md.getModifiers()); + CtClass[] plist = gen.makeParamList(md); + CtClass[] tlist = gen.makeThrowsList(md); + recordParams(plist, Modifier.isStatic(mod)); + md = p.parseMethod2(stable, md); + try { + if (md.isConstructor()) { + CtConstructor cons = new CtConstructor(plist, + gen.getThisClass()); + cons.setModifiers(mod); + md.accept(gen); + cons.getMethodInfo().setCodeAttribute( + bytecode.toCodeAttribute()); + cons.setExceptionTypes(tlist); + return cons; + } + else { + Declarator r = md.getReturn(); + CtClass rtype = gen.resolver.lookupClass(r); + recordReturnType(rtype, false); + CtMethod method = new CtMethod(rtype, r.getVariable().get(), + plist, gen.getThisClass()); + method.setModifiers(mod); + gen.setThisMethod(method); + md.accept(gen); + if (md.getBody() != null) + method.getMethodInfo().setCodeAttribute( + bytecode.toCodeAttribute()); + else + method.setModifiers(mod | Modifier.ABSTRACT); + + method.setExceptionTypes(tlist); + return method; + } + } + catch (NotFoundException e) { + throw new CompileError(e.toString()); + } + } + + /** + * Compiles a method (or constructor) body. + * + * @param src a single statement or a block. + * If null, this method produces a body returning zero or null. + */ + public Bytecode compileBody(CtBehavior method, String src) + throws CompileError + { + try { + int mod = method.getModifiers(); + recordParams(method.getParameterTypes(), Modifier.isStatic(mod)); + + CtClass rtype; + if (method instanceof CtMethod) { + gen.setThisMethod((CtMethod)method); + rtype = ((CtMethod)method).getReturnType(); + } + else + rtype = CtClass.voidType; + + recordReturnType(rtype, false); + boolean isVoid = rtype == CtClass.voidType; + + if (src == null) + makeDefaultBody(bytecode, rtype); + else { + Parser p = new Parser(new Lex(src)); + SymbolTable stb = new SymbolTable(stable); + Stmnt s = p.parseStatement(stb); + if (p.hasMore()) + throw new CompileError( + "the method/constructor body must be surrounded by {}"); + + boolean callSuper = false; + if (method instanceof CtConstructor) + callSuper = !((CtConstructor)method).isClassInitializer(); + + gen.atMethodBody(s, callSuper, isVoid); + } + + return bytecode; + } + catch (NotFoundException e) { + throw new CompileError(e.toString()); + } + } + + private static void makeDefaultBody(Bytecode b, CtClass type) { + int op; + int value; + if (type instanceof CtPrimitiveType) { + CtPrimitiveType pt = (CtPrimitiveType)type; + op = pt.getReturnOp(); + if (op == Opcode.DRETURN) + value = Opcode.DCONST_0; + else if (op == Opcode.FRETURN) + value = Opcode.FCONST_0; + else if (op == Opcode.LRETURN) + value = Opcode.LCONST_0; + else if (op == Opcode.RETURN) + value = Opcode.NOP; + else + value = Opcode.ICONST_0; + } + else { + op = Opcode.ARETURN; + value = Opcode.ACONST_NULL; + } + + if (value != Opcode.NOP) + b.addOpcode(value); + + b.addOpcode(op); + } + + /** + * Records local variables available at the specified program counter. + * If the LocalVariableAttribute is not available, this method does not + * record any local variable. It only returns false. + * + * @param pc program counter (>= 0) + * @return false if the CodeAttribute does not include a + * LocalVariableAttribute. + */ + public boolean recordLocalVariables(CodeAttribute ca, int pc) + throws CompileError + { + LocalVariableAttribute va + = (LocalVariableAttribute) + ca.getAttribute(LocalVariableAttribute.tag); + if (va == null) + return false; + + int n = va.tableLength(); + for (int i = 0; i < n; ++i) { + int start = va.startPc(i); + int len = va.codeLength(i); + if (start <= pc && pc < start + len) + gen.recordVariable(va.descriptor(i), va.variableName(i), + va.index(i), stable); + } + + return true; + } + + /** + * Records parameter names if the LocalVariableAttribute is available. + * It returns false unless the LocalVariableAttribute is available. + * + * @param numOfLocalVars the number of local variables used + * for storing the parameters. + * @return false if the CodeAttribute does not include a + * LocalVariableAttribute. + */ + public boolean recordParamNames(CodeAttribute ca, int numOfLocalVars) + throws CompileError + { + LocalVariableAttribute va + = (LocalVariableAttribute) + ca.getAttribute(LocalVariableAttribute.tag); + if (va == null) + return false; + + int n = va.tableLength(); + for (int i = 0; i < n; ++i) { + int index = va.index(i); + if (index < numOfLocalVars) + gen.recordVariable(va.descriptor(i), va.variableName(i), + index, stable); + } + + return true; + } + + + /** + * Makes variables $0 (this), $1, $2, ..., and $args represent method + * parameters. $args represents an array of all the parameters. + * It also makes $$ available as a parameter list of method call. + * + *

This must be called before calling compileStmnt() and + * compileExpr(). The correct value of + * isStatic must be recorded before compilation. + * maxLocals is updated to include $0,... + */ + public int recordParams(CtClass[] params, boolean isStatic) + throws CompileError + { + return gen.recordParams(params, isStatic, "$", "$args", "$$", stable); + } + + /** + * Makes variables $0, $1, $2, ..., and $args represent method + * parameters. $args represents an array of all the parameters. + * It also makes $$ available as a parameter list of method call. + * $0 can represent a local variable other than THIS (variable 0). + * $class is also made available. + * + *

This must be called before calling compileStmnt() and + * compileExpr(). The correct value of + * isStatic must be recorded before compilation. + * maxLocals is updated to include $0,... + * + * @param use0 true if $0 is used. + * @param varNo the register number of $0 (use0 is true) + * or $1 (otherwise). + * @param target the type of $0 (it can be null if use0 is false). + * It is used as the name of the type represented + * by $class. + * @param isStatic true if the method in which the compiled bytecode + * is embedded is static. + */ + public int recordParams(String target, CtClass[] params, + boolean use0, int varNo, boolean isStatic) + throws CompileError + { + return gen.recordParams(params, isStatic, "$", "$args", "$$", + use0, varNo, target, stable); + } + + /** + * Sets maxLocals to max. + * This method tells the compiler the local variables that have been + * allocated for the rest of the code. When the compiler needs + * new local variables, the local variables at the index max, + * max + 1, ... are assigned. + * + *

This method is indirectly called by recordParams. + */ + public void setMaxLocals(int max) { + gen.setMaxLocals(max); + } + + /** + * Prepares to use cast $r, $w, $_, and $type. + * $type is made to represent the specified return type. + * It also enables to write a return statement with a return value + * for void method. + * + *

If the return type is void, ($r) does nothing. + * The type of $_ is java.lang.Object. + * + * @param type the return type. + * @param useResultVar true if $_ is used. + * @return -1 or the variable index assigned to $_. + * @see #recordType(CtClass) + */ + public int recordReturnType(CtClass type, boolean useResultVar) + throws CompileError + { + gen.recordType(type); + return gen.recordReturnType(type, "$r", + (useResultVar ? resultVarName : null), stable); + } + + /** + * Prepares to use $type. Note that recordReturnType() overwrites + * the value of $type. + * + * @param t the type represented by $type. + */ + public void recordType(CtClass t) { + gen.recordType(t); + } + + /** + * Makes the given variable available. + * + * @param type variable type + * @param name variable name + */ + public int recordVariable(CtClass type, String name) + throws CompileError + { + return gen.recordVariable(type, name, stable); + } + + /** + * Prepares to use $proceed(). + * If the return type of $proceed() is void, null is pushed on the + * stack. + * + * @param target an expression specifying the target object. + * if null, "this" is the target. + * @param method the method name. + */ + public void recordProceed(String target, String method) + throws CompileError + { + Parser p = new Parser(new Lex(target)); + final ASTree texpr = p.parseExpression(stable); + final String m = method; + + ProceedHandler h = new ProceedHandler() { + public void doit(JvstCodeGen gen, Bytecode b, ASTList args) + throws CompileError + { + ASTree expr = new Member(m); + if (texpr != null) + expr = Expr.make('.', texpr, expr); + + expr = CallExpr.makeCall(expr, args); + gen.compileExpr(expr); + gen.addNullIfVoid(); + } + + public void setReturnType(JvstTypeChecker check, ASTList args) + throws CompileError + { + ASTree expr = new Member(m); + if (texpr != null) + expr = Expr.make('.', texpr, expr); + + expr = CallExpr.makeCall(expr, args); + expr.accept(check); + check.addNullIfVoid(); + } + }; + + gen.setProceedHandler(h, proceedName); + } + + /** + * Prepares to use $proceed() representing a static method. + * If the return type of $proceed() is void, null is pushed on the + * stack. + * + * @param targetClass the fully-qualified dot-separated name + * of the class declaring the method. + * @param method the method name. + */ + public void recordStaticProceed(String targetClass, String method) + throws CompileError + { + final String c = targetClass; + final String m = method; + + ProceedHandler h = new ProceedHandler() { + public void doit(JvstCodeGen gen, Bytecode b, ASTList args) + throws CompileError + { + Expr expr = Expr.make(TokenId.MEMBER, + new Symbol(c), new Member(m)); + expr = CallExpr.makeCall(expr, args); + gen.compileExpr(expr); + gen.addNullIfVoid(); + } + + public void setReturnType(JvstTypeChecker check, ASTList args) + throws CompileError + { + Expr expr = Expr.make(TokenId.MEMBER, + new Symbol(c), new Member(m)); + expr = CallExpr.makeCall(expr, args); + expr.accept(check); + check.addNullIfVoid(); + } + }; + + gen.setProceedHandler(h, proceedName); + } + + /** + * Prepares to use $proceed() representing a private/super's method. + * If the return type of $proceed() is void, null is pushed on the + * stack. This method is for methods invoked by INVOKESPECIAL. + * + * @param target an expression specifying the target object. + * if null, "this" is the target. + * @param classname the class name declaring the method. + * @param methodname the method name. + * @param descriptor the method descriptor. + */ + public void recordSpecialProceed(String target, String classname, + String methodname, String descriptor) + throws CompileError + { + Parser p = new Parser(new Lex(target)); + final ASTree texpr = p.parseExpression(stable); + final String cname = classname; + final String method = methodname; + final String desc = descriptor; + + ProceedHandler h = new ProceedHandler() { + public void doit(JvstCodeGen gen, Bytecode b, ASTList args) + throws CompileError + { + gen.compileInvokeSpecial(texpr, cname, method, desc, args); + } + + public void setReturnType(JvstTypeChecker c, ASTList args) + throws CompileError + { + c.compileInvokeSpecial(texpr, cname, method, desc, args); + } + + }; + + gen.setProceedHandler(h, proceedName); + } + + /** + * Prepares to use $proceed(). + */ + public void recordProceed(ProceedHandler h) { + gen.setProceedHandler(h, proceedName); + } + + /** + * Compiles a statement (or a block). + * recordParams() must be called before invoking + * this method. + * + *

Local variables that are not declared + * in the compiled source text might not be accessible within that + * source text. Fields and method parameters ($0, $1, ..) are available. + */ + public void compileStmnt(String src) throws CompileError { + Parser p = new Parser(new Lex(src)); + SymbolTable stb = new SymbolTable(stable); + while (p.hasMore()) { + Stmnt s = p.parseStatement(stb); + if (s != null) + s.accept(gen); + } + } + + /** + * Compiles an exression. recordParams() must be + * called before invoking this method. + * + *

Local variables are not accessible + * within the compiled source text. Fields and method parameters + * ($0, $1, ..) are available if recordParams() + * have been invoked. + */ + public void compileExpr(String src) throws CompileError { + ASTree e = parseExpr(src, stable); + compileExpr(e); + } + + /** + * Parsers an expression. + */ + public static ASTree parseExpr(String src, SymbolTable st) + throws CompileError + { + Parser p = new Parser(new Lex(src)); + return p.parseExpression(st); + } + + /** + * Compiles an exression. recordParams() must be + * called before invoking this method. + * + *

Local variables are not accessible + * within the compiled source text. Fields and method parameters + * ($0, $1, ..) are available if recordParams() + * have been invoked. + */ + public void compileExpr(ASTree e) throws CompileError { + if (e != null) + gen.compileExpr(e); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java b/src/main/java/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java new file mode 100644 index 0000000..7eb03ab --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java @@ -0,0 +1,710 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.ast.*; + +/* Code generator accepting extended Java syntax for Javassist. + */ + +public class JvstCodeGen extends MemberCodeGen { + String paramArrayName = null; + String paramListName = null; + CtClass[] paramTypeList = null; + private int paramVarBase = 0; // variable index for $0 or $1. + private boolean useParam0 = false; // true if $0 is used. + private String param0Type = null; // JVM name + public static final String sigName = "$sig"; + public static final String dollarTypeName = "$type"; + public static final String clazzName = "$class"; + private CtClass dollarType = null; + CtClass returnType = null; + String returnCastName = null; + private String returnVarName = null; // null if $_ is not used. + public static final String wrapperCastName = "$w"; + String proceedName = null; + public static final String cflowName = "$cflow"; + ProceedHandler procHandler = null; // null if not used. + + public JvstCodeGen(Bytecode b, CtClass cc, ClassPool cp) { + super(b, cc, cp); + setTypeChecker(new JvstTypeChecker(cc, cp, this)); + } + + /* Index of $1. + */ + private int indexOfParam1() { + return paramVarBase + (useParam0 ? 1 : 0); + } + + /* Records a ProceedHandler obejct. + * + * @param name the name of the special method call. + * it is usually $proceed. + */ + public void setProceedHandler(ProceedHandler h, String name) { + proceedName = name; + procHandler = h; + } + + /* If the type of the expression compiled last is void, + * add ACONST_NULL and change exprType, arrayDim, className. + */ + public void addNullIfVoid() { + if (exprType == VOID) { + bytecode.addOpcode(ACONST_NULL); + exprType = CLASS; + arrayDim = 0; + className = jvmJavaLangObject; + } + } + + /* To support $args, $sig, and $type. + * $args is an array of parameter list. + */ + public void atMember(Member mem) throws CompileError { + String name = mem.get(); + if (name.equals(paramArrayName)) { + compileParameterList(bytecode, paramTypeList, indexOfParam1()); + exprType = CLASS; + arrayDim = 1; + className = jvmJavaLangObject; + } + else if (name.equals(sigName)) { + bytecode.addLdc(Descriptor.ofMethod(returnType, paramTypeList)); + bytecode.addInvokestatic("javassist/runtime/Desc", "getParams", + "(Ljava/lang/String;)[Ljava/lang/Class;"); + exprType = CLASS; + arrayDim = 1; + className = "java/lang/Class"; + } + else if (name.equals(dollarTypeName)) { + if (dollarType == null) + throw new CompileError(dollarTypeName + " is not available"); + + bytecode.addLdc(Descriptor.of(dollarType)); + callGetType("getType"); + } + else if (name.equals(clazzName)) { + if (param0Type == null) + throw new CompileError(clazzName + " is not available"); + + bytecode.addLdc(param0Type); + callGetType("getClazz"); + } + else + super.atMember(mem); + } + + private void callGetType(String method) { + bytecode.addInvokestatic("javassist/runtime/Desc", method, + "(Ljava/lang/String;)Ljava/lang/Class;"); + exprType = CLASS; + arrayDim = 0; + className = "java/lang/Class"; + } + + protected void atFieldAssign(Expr expr, int op, ASTree left, + ASTree right, boolean doDup) throws CompileError + { + if (left instanceof Member + && ((Member)left).get().equals(paramArrayName)) { + if (op != '=') + throw new CompileError("bad operator for " + paramArrayName); + + right.accept(this); + if (arrayDim != 1 || exprType != CLASS) + throw new CompileError("invalid type for " + paramArrayName); + + atAssignParamList(paramTypeList, bytecode); + if (!doDup) + bytecode.addOpcode(POP); + } + else + super.atFieldAssign(expr, op, left, right, doDup); + } + + protected void atAssignParamList(CtClass[] params, Bytecode code) + throws CompileError + { + if (params == null) + return; + + int varNo = indexOfParam1(); + int n = params.length; + for (int i = 0; i < n; ++i) { + code.addOpcode(DUP); + code.addIconst(i); + code.addOpcode(AALOAD); + compileUnwrapValue(params[i], code); + code.addStore(varNo, params[i]); + varNo += is2word(exprType, arrayDim) ? 2 : 1; + } + } + + public void atCastExpr(CastExpr expr) throws CompileError { + ASTList classname = expr.getClassName(); + if (classname != null && expr.getArrayDim() == 0) { + ASTree p = classname.head(); + if (p instanceof Symbol && classname.tail() == null) { + String typename = ((Symbol)p).get(); + if (typename.equals(returnCastName)) { + atCastToRtype(expr); + return; + } + else if (typename.equals(wrapperCastName)) { + atCastToWrapper(expr); + return; + } + } + } + + super.atCastExpr(expr); + } + + /** + * Inserts a cast operator to the return type. + * If the return type is void, this does nothing. + */ + protected void atCastToRtype(CastExpr expr) throws CompileError { + expr.getOprand().accept(this); + if (exprType == VOID || isRefType(exprType) || arrayDim > 0) + compileUnwrapValue(returnType, bytecode); + else if (returnType instanceof CtPrimitiveType) { + CtPrimitiveType pt = (CtPrimitiveType)returnType; + int destType = MemberResolver.descToType(pt.getDescriptor()); + atNumCastExpr(exprType, destType); + exprType = destType; + arrayDim = 0; + className = null; + } + else + throw new CompileError("invalid cast"); + } + + protected void atCastToWrapper(CastExpr expr) throws CompileError { + expr.getOprand().accept(this); + if (isRefType(exprType) || arrayDim > 0) + return; // Object type. do nothing. + + CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); + if (clazz instanceof CtPrimitiveType) { + CtPrimitiveType pt = (CtPrimitiveType)clazz; + String wrapper = pt.getWrapperName(); + bytecode.addNew(wrapper); // new + bytecode.addOpcode(DUP); // dup + if (pt.getDataSize() > 1) + bytecode.addOpcode(DUP2_X2); // dup2_x2 + else + bytecode.addOpcode(DUP2_X1); // dup2_x1 + + bytecode.addOpcode(POP2); // pop2 + bytecode.addInvokespecial(wrapper, "", + "(" + pt.getDescriptor() + ")V"); + // invokespecial + exprType = CLASS; + arrayDim = 0; + className = jvmJavaLangObject; + } + } + + /* Delegates to a ProcHandler object if the method call is + * $proceed(). It may process $cflow(). + */ + public void atCallExpr(CallExpr expr) throws CompileError { + ASTree method = expr.oprand1(); + if (method instanceof Member) { + String name = ((Member)method).get(); + if (procHandler != null && name.equals(proceedName)) { + procHandler.doit(this, bytecode, (ASTList)expr.oprand2()); + return; + } + else if (name.equals(cflowName)) { + atCflow((ASTList)expr.oprand2()); + return; + } + } + + super.atCallExpr(expr); + } + + /* To support $cflow(). + */ + protected void atCflow(ASTList cname) throws CompileError { + StringBuffer sbuf = new StringBuffer(); + if (cname == null || cname.tail() != null) + throw new CompileError("bad " + cflowName); + + makeCflowName(sbuf, cname.head()); + String name = sbuf.toString(); + Object[] names = resolver.getClassPool().lookupCflow(name); + if (names == null) + throw new CompileError("no such " + cflowName + ": " + name); + + bytecode.addGetstatic((String)names[0], (String)names[1], + "Ljavassist/runtime/Cflow;"); + bytecode.addInvokevirtual("javassist.runtime.Cflow", + "value", "()I"); + exprType = INT; + arrayDim = 0; + className = null; + } + + /* Syntax: + * + * : $cflow '(' ')' + * : ('.' )* + */ + private static void makeCflowName(StringBuffer sbuf, ASTree name) + throws CompileError + { + if (name instanceof Symbol) { + sbuf.append(((Symbol)name).get()); + return; + } + else if (name instanceof Expr) { + Expr expr = (Expr)name; + if (expr.getOperator() == '.') { + makeCflowName(sbuf, expr.oprand1()); + sbuf.append('.'); + makeCflowName(sbuf, expr.oprand2()); + return; + } + } + + throw new CompileError("bad " + cflowName); + } + + /* To support $$. ($$) is equivalent to ($1, ..., $n). + * It can be used only as a parameter list of method call. + */ + public boolean isParamListName(ASTList args) { + if (paramTypeList != null + && args != null && args.tail() == null) { + ASTree left = args.head(); + return (left instanceof Member + && ((Member)left).get().equals(paramListName)); + } + else + return false; + } + + /* + public int getMethodArgsLength(ASTList args) { + if (!isParamListName(args)) + return super.getMethodArgsLength(args); + + return paramTypeList.length; + } + */ + + public int getMethodArgsLength(ASTList args) { + String pname = paramListName; + int n = 0; + while (args != null) { + ASTree a = args.head(); + if (a instanceof Member && ((Member)a).get().equals(pname)) { + if (paramTypeList != null) + n += paramTypeList.length; + } + else + ++n; + + args = args.tail(); + } + + return n; + } + + public void atMethodArgs(ASTList args, int[] types, int[] dims, + String[] cnames) throws CompileError { + CtClass[] params = paramTypeList; + String pname = paramListName; + int i = 0; + while (args != null) { + ASTree a = args.head(); + if (a instanceof Member && ((Member)a).get().equals(pname)) { + if (params != null) { + int n = params.length; + int regno = indexOfParam1(); + for (int k = 0; k < n; ++k) { + CtClass p = params[k]; + regno += bytecode.addLoad(regno, p); + setType(p); + types[i] = exprType; + dims[i] = arrayDim; + cnames[i] = className; + ++i; + } + } + } + else { + a.accept(this); + types[i] = exprType; + dims[i] = arrayDim; + cnames[i] = className; + ++i; + } + + args = args.tail(); + } + } + + /* + public void atMethodArgs(ASTList args, int[] types, int[] dims, + String[] cnames) throws CompileError { + if (!isParamListName(args)) { + super.atMethodArgs(args, types, dims, cnames); + return; + } + + CtClass[] params = paramTypeList; + if (params == null) + return; + + int n = params.length; + int regno = indexOfParam1(); + for (int i = 0; i < n; ++i) { + CtClass p = params[i]; + regno += bytecode.addLoad(regno, p); + setType(p); + types[i] = exprType; + dims[i] = arrayDim; + cnames[i] = className; + } + } + */ + + /* called by Javac#recordSpecialProceed(). + */ + void compileInvokeSpecial(ASTree target, String classname, + String methodname, String descriptor, + ASTList args) + throws CompileError + { + target.accept(this); + int nargs = getMethodArgsLength(args); + atMethodArgs(args, new int[nargs], new int[nargs], + new String[nargs]); + bytecode.addInvokespecial(classname, methodname, descriptor); + setReturnType(descriptor, false, false); + addNullIfVoid(); + } + + /* + * Makes it valid to write "return ;" for a void method. + */ + protected void atReturnStmnt(Stmnt st) throws CompileError { + ASTree result = st.getLeft(); + if (result != null && returnType == CtClass.voidType) { + compileExpr(result); + if (is2word(exprType, arrayDim)) + bytecode.addOpcode(POP2); + else if (exprType != VOID) + bytecode.addOpcode(POP); + + result = null; + } + + atReturnStmnt2(result); + } + + /** + * Makes a cast to the return type ($r) available. + * It also enables $_. + * + *

If the return type is void, ($r) does nothing. + * The type of $_ is java.lang.Object. + * + * @param resultName null if $_ is not used. + * @return -1 or the variable index assigned to $_. + */ + public int recordReturnType(CtClass type, String castName, + String resultName, SymbolTable tbl) throws CompileError + { + returnType = type; + returnCastName = castName; + returnVarName = resultName; + if (resultName == null) + return -1; + else { + int varNo = getMaxLocals(); + int locals = varNo + recordVar(type, resultName, varNo, tbl); + setMaxLocals(locals); + return varNo; + } + } + + /** + * Makes $type available. + */ + public void recordType(CtClass t) { + dollarType = t; + } + + /** + * Makes method parameters $0, $1, ..., $args, $$, and $class available. + * $0 is equivalent to THIS if the method is not static. Otherwise, + * if the method is static, then $0 is not available. + */ + public int recordParams(CtClass[] params, boolean isStatic, + String prefix, String paramVarName, + String paramsName, SymbolTable tbl) + throws CompileError + { + return recordParams(params, isStatic, prefix, paramVarName, + paramsName, !isStatic, 0, getThisName(), tbl); + } + + /** + * Makes method parameters $0, $1, ..., $args, $$, and $class available. + * $0 is available only if use0 is true. It might not be equivalent + * to THIS. + * + * @param params the parameter types (the types of $1, $2, ..) + * @param prefix it must be "$" (the first letter of $0, $1, ...) + * @param paramVarName it must be "$args" + * @param paramsName it must be "$$" + * @param use0 true if $0 is used. + * @param paramBase the register number of $0 (use0 is true) + * or $1 (otherwise). + * @param target the class of $0. If use0 is false, target + * can be null. The value of "target" is also used + * as the name of the type represented by $class. + * @param isStatic true if the method in which the compiled bytecode + * is embedded is static. + */ + public int recordParams(CtClass[] params, boolean isStatic, + String prefix, String paramVarName, + String paramsName, boolean use0, + int paramBase, String target, + SymbolTable tbl) + throws CompileError + { + int varNo; + + paramTypeList = params; + paramArrayName = paramVarName; + paramListName = paramsName; + paramVarBase = paramBase; + useParam0 = use0; + + if (target != null) + param0Type = MemberResolver.jvmToJavaName(target); + + inStaticMethod = isStatic; + varNo = paramBase; + if (use0) { + String varName = prefix + "0"; + Declarator decl + = new Declarator(CLASS, MemberResolver.javaToJvmName(target), + 0, varNo++, new Symbol(varName)); + tbl.append(varName, decl); + } + + for (int i = 0; i < params.length; ++i) + varNo += recordVar(params[i], prefix + (i + 1), varNo, tbl); + + if (getMaxLocals() < varNo) + setMaxLocals(varNo); + + return varNo; + } + + /** + * Makes the given variable name available. + * + * @param type variable type + * @param varName variable name + */ + public int recordVariable(CtClass type, String varName, SymbolTable tbl) + throws CompileError + { + if (varName == null) + return -1; + else { + int varNo = getMaxLocals(); + int locals = varNo + recordVar(type, varName, varNo, tbl); + setMaxLocals(locals); + return varNo; + } + } + + private int recordVar(CtClass cc, String varName, int varNo, + SymbolTable tbl) throws CompileError + { + if (cc == CtClass.voidType) { + exprType = CLASS; + arrayDim = 0; + className = jvmJavaLangObject; + } + else + setType(cc); + + Declarator decl + = new Declarator(exprType, className, arrayDim, + varNo, new Symbol(varName)); + tbl.append(varName, decl); + return is2word(exprType, arrayDim) ? 2 : 1; + } + + /** + * Makes the given variable name available. + * + * @param typeDesc the type descriptor of the variable + * @param varName variable name + * @param varNo an index into the local variable array + */ + public void recordVariable(String typeDesc, String varName, int varNo, + SymbolTable tbl) throws CompileError + { + char c; + int dim = 0; + while ((c = typeDesc.charAt(dim)) == '[') + ++dim; + + int type = MemberResolver.descToType(c); + String cname = null; + if (type == CLASS) { + if (dim == 0) + cname = typeDesc.substring(1, typeDesc.length() - 1); + else + cname = typeDesc.substring(dim + 1, typeDesc.length() - 1); + } + + Declarator decl + = new Declarator(type, cname, dim, varNo, new Symbol(varName)); + tbl.append(varName, decl); + } + + /* compileParameterList() returns the stack size used + * by the produced code. + * + * This method correctly computes the max_stack value. + * + * @param regno the index of the local variable in which + * the first argument is received. + * (0: static method, 1: regular method.) + */ + public static int compileParameterList(Bytecode code, + CtClass[] params, int regno) { + if (params == null) { + code.addIconst(0); // iconst_0 + code.addAnewarray(javaLangObject); // anewarray Object + return 1; + } + else { + CtClass[] args = new CtClass[1]; + int n = params.length; + code.addIconst(n); // iconst_ + code.addAnewarray(javaLangObject); // anewarray Object + for (int i = 0; i < n; ++i) { + code.addOpcode(Bytecode.DUP); // dup + code.addIconst(i); // iconst_ + if (params[i].isPrimitive()) { + CtPrimitiveType pt = (CtPrimitiveType)params[i]; + String wrapper = pt.getWrapperName(); + code.addNew(wrapper); // new + code.addOpcode(Bytecode.DUP); // dup + int s = code.addLoad(regno, pt); // ?load + regno += s; + args[0] = pt; + code.addInvokespecial(wrapper, "", + Descriptor.ofMethod(CtClass.voidType, args)); + // invokespecial + } + else { + code.addAload(regno); // aload + ++regno; + } + + code.addOpcode(Bytecode.AASTORE); // aastore + } + + return 8; + } + } + + protected void compileUnwrapValue(CtClass type, Bytecode code) + throws CompileError + { + if (type == CtClass.voidType) { + addNullIfVoid(); + return; + } + + if (exprType == VOID) + throw new CompileError("invalid type for " + returnCastName); + + if (type instanceof CtPrimitiveType) { + CtPrimitiveType pt = (CtPrimitiveType)type; + // pt is not voidType. + String wrapper = pt.getWrapperName(); + code.addCheckcast(wrapper); + code.addInvokevirtual(wrapper, pt.getGetMethodName(), + pt.getGetMethodDescriptor()); + setType(type); + } + else { + code.addCheckcast(type); + setType(type); + } + } + + /* Sets exprType, arrayDim, and className; + * If type is void, then this method does nothing. + */ + public void setType(CtClass type) throws CompileError { + setType(type, 0); + } + + private void setType(CtClass type, int dim) throws CompileError { + if (type.isPrimitive()) { + CtPrimitiveType pt = (CtPrimitiveType)type; + exprType = MemberResolver.descToType(pt.getDescriptor()); + arrayDim = dim; + className = null; + } + else if (type.isArray()) + try { + setType(type.getComponentType(), dim + 1); + } + catch (NotFoundException e) { + throw new CompileError("undefined type: " + type.getName()); + } + else { + exprType = CLASS; + arrayDim = dim; + className = MemberResolver.javaToJvmName(type.getName()); + } + } + + /* Performs implicit coercion from exprType to type. + */ + public void doNumCast(CtClass type) throws CompileError { + if (arrayDim == 0 && !isRefType(exprType)) + if (type instanceof CtPrimitiveType) { + CtPrimitiveType pt = (CtPrimitiveType)type; + atNumCastExpr(exprType, + MemberResolver.descToType(pt.getDescriptor())); + } + else + throw new CompileError("type mismatch"); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java b/src/main/java/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java new file mode 100644 index 0000000..a28aaad --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java @@ -0,0 +1,282 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.compiler.ast.*; + +/* Type checker accepting extended Java syntax for Javassist. + */ + +public class JvstTypeChecker extends TypeChecker { + private JvstCodeGen codeGen; + + public JvstTypeChecker(CtClass cc, ClassPool cp, JvstCodeGen gen) { + super(cc, cp); + codeGen = gen; + } + + /* If the type of the expression compiled last is void, + * add ACONST_NULL and change exprType, arrayDim, className. + */ + public void addNullIfVoid() { + if (exprType == VOID) { + exprType = CLASS; + arrayDim = 0; + className = jvmJavaLangObject; + } + } + + /* To support $args, $sig, and $type. + * $args is an array of parameter list. + */ + public void atMember(Member mem) throws CompileError { + String name = mem.get(); + if (name.equals(codeGen.paramArrayName)) { + exprType = CLASS; + arrayDim = 1; + className = jvmJavaLangObject; + } + else if (name.equals(JvstCodeGen.sigName)) { + exprType = CLASS; + arrayDim = 1; + className = "java/lang/Class"; + } + else if (name.equals(JvstCodeGen.dollarTypeName) + || name.equals(JvstCodeGen.clazzName)) { + exprType = CLASS; + arrayDim = 0; + className = "java/lang/Class"; + } + else + super.atMember(mem); + } + + protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) + throws CompileError + { + if (left instanceof Member + && ((Member)left).get().equals(codeGen.paramArrayName)) { + right.accept(this); + CtClass[] params = codeGen.paramTypeList; + if (params == null) + return; + + int n = params.length; + for (int i = 0; i < n; ++i) + compileUnwrapValue(params[i]); + } + else + super.atFieldAssign(expr, op, left, right); + } + + public void atCastExpr(CastExpr expr) throws CompileError { + ASTList classname = expr.getClassName(); + if (classname != null && expr.getArrayDim() == 0) { + ASTree p = classname.head(); + if (p instanceof Symbol && classname.tail() == null) { + String typename = ((Symbol)p).get(); + if (typename.equals(codeGen.returnCastName)) { + atCastToRtype(expr); + return; + } + else if (typename.equals(JvstCodeGen.wrapperCastName)) { + atCastToWrapper(expr); + return; + } + } + } + + super.atCastExpr(expr); + } + + /** + * Inserts a cast operator to the return type. + * If the return type is void, this does nothing. + */ + protected void atCastToRtype(CastExpr expr) throws CompileError { + CtClass returnType = codeGen.returnType; + expr.getOprand().accept(this); + if (exprType == VOID || CodeGen.isRefType(exprType) || arrayDim > 0) + compileUnwrapValue(returnType); + else if (returnType instanceof CtPrimitiveType) { + CtPrimitiveType pt = (CtPrimitiveType)returnType; + int destType = MemberResolver.descToType(pt.getDescriptor()); + exprType = destType; + arrayDim = 0; + className = null; + } + } + + protected void atCastToWrapper(CastExpr expr) throws CompileError { + expr.getOprand().accept(this); + if (CodeGen.isRefType(exprType) || arrayDim > 0) + return; // Object type. do nothing. + + CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); + if (clazz instanceof CtPrimitiveType) { + exprType = CLASS; + arrayDim = 0; + className = jvmJavaLangObject; + } + } + + /* Delegates to a ProcHandler object if the method call is + * $proceed(). It may process $cflow(). + */ + public void atCallExpr(CallExpr expr) throws CompileError { + ASTree method = expr.oprand1(); + if (method instanceof Member) { + String name = ((Member)method).get(); + if (codeGen.procHandler != null + && name.equals(codeGen.proceedName)) { + codeGen.procHandler.setReturnType(this, + (ASTList)expr.oprand2()); + return; + } + else if (name.equals(JvstCodeGen.cflowName)) { + atCflow((ASTList)expr.oprand2()); + return; + } + } + + super.atCallExpr(expr); + } + + /* To support $cflow(). + */ + protected void atCflow(ASTList cname) throws CompileError { + exprType = INT; + arrayDim = 0; + className = null; + } + + /* To support $$. ($$) is equivalent to ($1, ..., $n). + * It can be used only as a parameter list of method call. + */ + public boolean isParamListName(ASTList args) { + if (codeGen.paramTypeList != null + && args != null && args.tail() == null) { + ASTree left = args.head(); + return (left instanceof Member + && ((Member)left).get().equals(codeGen.paramListName)); + } + else + return false; + } + + public int getMethodArgsLength(ASTList args) { + String pname = codeGen.paramListName; + int n = 0; + while (args != null) { + ASTree a = args.head(); + if (a instanceof Member && ((Member)a).get().equals(pname)) { + if (codeGen.paramTypeList != null) + n += codeGen.paramTypeList.length; + } + else + ++n; + + args = args.tail(); + } + + return n; + } + + public void atMethodArgs(ASTList args, int[] types, int[] dims, + String[] cnames) throws CompileError { + CtClass[] params = codeGen.paramTypeList; + String pname = codeGen.paramListName; + int i = 0; + while (args != null) { + ASTree a = args.head(); + if (a instanceof Member && ((Member)a).get().equals(pname)) { + if (params != null) { + int n = params.length; + for (int k = 0; k < n; ++k) { + CtClass p = params[k]; + setType(p); + types[i] = exprType; + dims[i] = arrayDim; + cnames[i] = className; + ++i; + } + } + } + else { + a.accept(this); + types[i] = exprType; + dims[i] = arrayDim; + cnames[i] = className; + ++i; + } + + args = args.tail(); + } + } + + /* called by Javac#recordSpecialProceed(). + */ + void compileInvokeSpecial(ASTree target, String classname, + String methodname, String descriptor, + ASTList args) + throws CompileError + { + target.accept(this); + int nargs = getMethodArgsLength(args); + atMethodArgs(args, new int[nargs], new int[nargs], + new String[nargs]); + setReturnType(descriptor); + addNullIfVoid(); + } + + protected void compileUnwrapValue(CtClass type) throws CompileError + { + if (type == CtClass.voidType) + addNullIfVoid(); + else + setType(type); + } + + /* Sets exprType, arrayDim, and className; + * If type is void, then this method does nothing. + */ + public void setType(CtClass type) throws CompileError { + setType(type, 0); + } + + private void setType(CtClass type, int dim) throws CompileError { + if (type.isPrimitive()) { + CtPrimitiveType pt = (CtPrimitiveType)type; + exprType = MemberResolver.descToType(pt.getDescriptor()); + arrayDim = dim; + className = null; + } + else if (type.isArray()) + try { + setType(type.getComponentType(), dim + 1); + } + catch (NotFoundException e) { + throw new CompileError("undefined type: " + type.getName()); + } + else { + exprType = CLASS; + arrayDim = dim; + className = MemberResolver.javaToJvmName(type.getName()); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/KeywordTable.java b/src/main/java/com/wenshuo/agent/javassist/compiler/KeywordTable.java new file mode 100644 index 0000000..8f4aa18 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/KeywordTable.java @@ -0,0 +1,33 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +public final class KeywordTable extends java.util.HashMap { + public KeywordTable() { super(); } + + public int lookup(String name) { + Object found = get(name); + if (found == null) + return -1; + else + return ((Integer)found).intValue(); + } + + public void append(String name, int t) { + put(name, new Integer(t)); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/Lex.java b/src/main/java/com/wenshuo/agent/javassist/compiler/Lex.java new file mode 100644 index 0000000..f83e802 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/Lex.java @@ -0,0 +1,551 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +class Token { + public Token next = null; + public int tokenId; + + public long longValue; + public double doubleValue; + public String textValue; +} + +public class Lex implements TokenId { + private int lastChar; + private StringBuffer textBuffer; + private Token currentToken; + private Token lookAheadTokens; + + private String input; + private int position, maxlen, lineNumber; + + /** + * Constructs a lexical analyzer. + */ + public Lex(String s) { + lastChar = -1; + textBuffer = new StringBuffer(); + currentToken = new Token(); + lookAheadTokens = null; + + input = s; + position = 0; + maxlen = s.length(); + lineNumber = 0; + } + + public int get() { + if (lookAheadTokens == null) + return get(currentToken); + else { + Token t; + currentToken = t = lookAheadTokens; + lookAheadTokens = lookAheadTokens.next; + return t.tokenId; + } + } + + /** + * Looks at the next token. + */ + public int lookAhead() { + return lookAhead(0); + } + + public int lookAhead(int i) { + Token tk = lookAheadTokens; + if (tk == null) { + lookAheadTokens = tk = currentToken; // reuse an object! + tk.next = null; + get(tk); + } + + for (; i-- > 0; tk = tk.next) + if (tk.next == null) { + Token tk2; + tk.next = tk2 = new Token(); + get(tk2); + } + + currentToken = tk; + return tk.tokenId; + } + + public String getString() { + return currentToken.textValue; + } + + public long getLong() { + return currentToken.longValue; + } + + public double getDouble() { + return currentToken.doubleValue; + } + + private int get(Token token) { + int t; + do { + t = readLine(token); + } while (t == '\n'); + token.tokenId = t; + return t; + } + + private int readLine(Token token) { + int c = getNextNonWhiteChar(); + if(c < 0) + return c; + else if(c == '\n') { + ++lineNumber; + return '\n'; + } + else if (c == '\'') + return readCharConst(token); + else if (c == '"') + return readStringL(token); + else if ('0' <= c && c <= '9') + return readNumber(c, token); + else if(c == '.'){ + c = getc(); + if ('0' <= c && c <= '9') { + StringBuffer tbuf = textBuffer; + tbuf.setLength(0); + tbuf.append('.'); + return readDouble(tbuf, c, token); + } + else{ + ungetc(c); + return readSeparator('.'); + } + } + else if (Character.isJavaIdentifierStart((char)c)) + return readIdentifier(c, token); + else + return readSeparator(c); + } + + private int getNextNonWhiteChar() { + int c; + do { + c = getc(); + if (c == '/') { + c = getc(); + if (c == '/') + do { + c = getc(); + } while (c != '\n' && c != '\r' && c != -1); + else if (c == '*') + while (true) { + c = getc(); + if (c == -1) + break; + else if (c == '*') + if ((c = getc()) == '/') { + c = ' '; + break; + } + else + ungetc(c); + } + else { + ungetc(c); + c = '/'; + } + } + } while(isBlank(c)); + return c; + } + + private int readCharConst(Token token) { + int c; + int value = 0; + while ((c = getc()) != '\'') + if (c == '\\') + value = readEscapeChar(); + else if (c < 0x20) { + if (c == '\n') + ++lineNumber; + + return BadToken; + } + else + value = c; + + token.longValue = value; + return CharConstant; + } + + private int readEscapeChar() { + int c = getc(); + if (c == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; + else if (c == 'r') + c = '\r'; + else if (c == 'f') + c = '\f'; + else if (c == '\n') + ++lineNumber; + + return c; + } + + private int readStringL(Token token) { + int c; + StringBuffer tbuf = textBuffer; + tbuf.setLength(0); + for (;;) { + while ((c = getc()) != '"') { + if (c == '\\') + c = readEscapeChar(); + else if (c == '\n' || c < 0) { + ++lineNumber; + return BadToken; + } + + tbuf.append((char)c); + } + + for (;;) { + c = getc(); + if (c == '\n') + ++lineNumber; + else if (!isBlank(c)) + break; + } + + if (c != '"') { + ungetc(c); + break; + } + } + + token.textValue = tbuf.toString(); + return StringL; + } + + private int readNumber(int c, Token token) { + long value = 0; + int c2 = getc(); + if (c == '0') + if (c2 == 'X' || c2 == 'x') + for (;;) { + c = getc(); + if ('0' <= c && c <= '9') + value = value * 16 + (long)(c - '0'); + else if ('A' <= c && c <= 'F') + value = value * 16 + (long)(c - 'A' + 10); + else if ('a' <= c && c <= 'f') + value = value * 16 + (long)(c - 'a' + 10); + else { + token.longValue = value; + if (c == 'L' || c == 'l') + return LongConstant; + else { + ungetc(c); + return IntConstant; + } + } + } + else if ('0' <= c2 && c2 <= '7') { + value = c2 - '0'; + for (;;) { + c = getc(); + if ('0' <= c && c <= '7') + value = value * 8 + (long)(c - '0'); + else { + token.longValue = value; + if (c == 'L' || c == 'l') + return LongConstant; + else { + ungetc(c); + return IntConstant; + } + } + } + } + + value = c - '0'; + while ('0' <= c2 && c2 <= '9') { + value = value * 10 + c2 - '0'; + c2 = getc(); + } + + token.longValue = value; + if (c2 == 'F' || c2 == 'f') { + token.doubleValue = (double)value; + return FloatConstant; + } + else if (c2 == 'E' || c2 == 'e' + || c2 == 'D' || c2 == 'd' || c2 == '.') { + StringBuffer tbuf = textBuffer; + tbuf.setLength(0); + tbuf.append(value); + return readDouble(tbuf, c2, token); + } + else if (c2 == 'L' || c2 == 'l') + return LongConstant; + else { + ungetc(c2); + return IntConstant; + } + } + + private int readDouble(StringBuffer sbuf, int c, Token token) { + if (c != 'E' && c != 'e' && c != 'D' && c != 'd') { + sbuf.append((char)c); + for (;;) { + c = getc(); + if ('0' <= c && c <= '9') + sbuf.append((char)c); + else + break; + } + } + + if (c == 'E' || c == 'e') { + sbuf.append((char)c); + c = getc(); + if (c == '+' || c == '-') { + sbuf.append((char)c); + c = getc(); + } + + while ('0' <= c && c <= '9') { + sbuf.append((char)c); + c = getc(); + } + } + + try { + token.doubleValue = Double.parseDouble(sbuf.toString()); + } + catch (NumberFormatException e) { + return BadToken; + } + + if (c == 'F' || c == 'f') + return FloatConstant; + else { + if (c != 'D' && c != 'd') + ungetc(c); + + return DoubleConstant; + } + } + + // !"#$%&'( )*+,-./0 12345678 9:;<=>? + private static final int[] equalOps + = { NEQ, 0, 0, 0, MOD_E, AND_E, 0, 0, + 0, MUL_E, PLUS_E, 0, MINUS_E, 0, DIV_E, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, LE, EQ, GE, 0 }; + + private int readSeparator(int c) { + int c2, c3; + if ('!' <= c && c <= '?') { + int t = equalOps[c - '!']; + if (t == 0) + return c; + else { + c2 = getc(); + if (c == c2) + switch (c) { + case '=' : + return EQ; + case '+' : + return PLUSPLUS; + case '-' : + return MINUSMINUS; + case '&' : + return ANDAND; + case '<' : + c3 = getc(); + if (c3 == '=') + return LSHIFT_E; + else { + ungetc(c3); + return LSHIFT; + } + case '>' : + c3 = getc(); + if (c3 == '=') + return RSHIFT_E; + else if (c3 == '>') { + c3 = getc(); + if (c3 == '=') + return ARSHIFT_E; + else { + ungetc(c3); + return ARSHIFT; + } + } + else { + ungetc(c3); + return RSHIFT; + } + default : + break; + } + else if (c2 == '=') + return t; + } + } + else if (c == '^') { + c2 = getc(); + if (c2 == '=') + return EXOR_E; + } + else if (c == '|') { + c2 = getc(); + if (c2 == '=') + return OR_E; + else if (c2 == '|') + return OROR; + } + else + return c; + + ungetc(c2); + return c; + } + + private int readIdentifier(int c, Token token) { + StringBuffer tbuf = textBuffer; + tbuf.setLength(0); + + do { + tbuf.append((char)c); + c = getc(); + } while (Character.isJavaIdentifierPart((char)c)); + + ungetc(c); + + String name = tbuf.toString(); + int t = ktable.lookup(name); + if (t >= 0) + return t; + else { + /* tbuf.toString() is executed quickly since it does not + * need memory copy. Using a hand-written extensible + * byte-array class instead of StringBuffer is not a good idea + * for execution speed. Converting a byte array to a String + * object is very slow. Using an extensible char array + * might be OK. + */ + token.textValue = name; + return Identifier; + } + } + + private static final KeywordTable ktable = new KeywordTable(); + + static { + ktable.append("abstract", ABSTRACT); + ktable.append("boolean", BOOLEAN); + ktable.append("break", BREAK); + ktable.append("byte", BYTE); + ktable.append("case", CASE); + ktable.append("catch", CATCH); + ktable.append("char", CHAR); + ktable.append("class", CLASS); + ktable.append("const", CONST); + ktable.append("continue", CONTINUE); + ktable.append("default", DEFAULT); + ktable.append("do", DO); + ktable.append("double", DOUBLE); + ktable.append("else", ELSE); + ktable.append("extends", EXTENDS); + ktable.append("false", FALSE); + ktable.append("final", FINAL); + ktable.append("finally", FINALLY); + ktable.append("float", FLOAT); + ktable.append("for", FOR); + ktable.append("goto", GOTO); + ktable.append("if", IF); + ktable.append("implements", IMPLEMENTS); + ktable.append("import", IMPORT); + ktable.append("instanceof", INSTANCEOF); + ktable.append("int", INT); + ktable.append("interface", INTERFACE); + ktable.append("long", LONG); + ktable.append("native", NATIVE); + ktable.append("new", NEW); + ktable.append("null", NULL); + ktable.append("package", PACKAGE); + ktable.append("private", PRIVATE); + ktable.append("protected", PROTECTED); + ktable.append("public", PUBLIC); + ktable.append("return", RETURN); + ktable.append("short", SHORT); + ktable.append("static", STATIC); + ktable.append("strictfp", STRICT); + ktable.append("super", SUPER); + ktable.append("switch", SWITCH); + ktable.append("synchronized", SYNCHRONIZED); + ktable.append("this", THIS); + ktable.append("throw", THROW); + ktable.append("throws", THROWS); + ktable.append("transient", TRANSIENT); + ktable.append("true", TRUE); + ktable.append("try", TRY); + ktable.append("void", VOID); + ktable.append("volatile", VOLATILE); + ktable.append("while", WHILE); + } + + private static boolean isBlank(int c) { + return c == ' ' || c == '\t' || c == '\f' || c == '\r' + || c == '\n'; + } + + private static boolean isDigit(int c) { + return '0' <= c && c <= '9'; + } + + private void ungetc(int c) { + lastChar = c; + } + + public String getTextAround() { + int begin = position - 10; + if (begin < 0) + begin = 0; + + int end = position + 10; + if (end > maxlen) + end = maxlen; + + return input.substring(begin, end); + } + + private int getc() { + if (lastChar < 0) + if (position < maxlen) + return input.charAt(position++); + else + return -1; + else { + int c = lastChar; + lastChar = -1; + return c; + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java b/src/main/java/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java new file mode 100644 index 0000000..91d3d28 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java @@ -0,0 +1,1160 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.ast.*; + +import java.util.ArrayList; + +/* Code generator methods depending on javassist.* classes. + */ +public class MemberCodeGen extends CodeGen { + protected MemberResolver resolver; + protected CtClass thisClass; + protected MethodInfo thisMethod; + + protected boolean resultStatic; + + public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) { + super(b); + resolver = new MemberResolver(cp); + thisClass = cc; + thisMethod = null; + } + + /** + * Returns the major version of the class file + * targeted by this compilation. + */ + public int getMajorVersion() { + ClassFile cf = thisClass.getClassFile2(); + if (cf == null) + return ClassFile.MAJOR_VERSION; // JDK 1.3 + else + return cf.getMajorVersion(); + } + + /** + * Records the currently compiled method. + */ + public void setThisMethod(CtMethod m) { + thisMethod = m.getMethodInfo2(); + if (typeChecker != null) + typeChecker.setThisMethod(thisMethod); + } + + public CtClass getThisClass() { return thisClass; } + + /** + * Returns the JVM-internal representation of this class name. + */ + protected String getThisName() { + return MemberResolver.javaToJvmName(thisClass.getName()); + } + + /** + * Returns the JVM-internal representation of this super class name. + */ + protected String getSuperName() throws CompileError { + return MemberResolver.javaToJvmName( + MemberResolver.getSuperclass(thisClass).getName()); + } + + protected void insertDefaultSuperCall() throws CompileError { + bytecode.addAload(0); + bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass), + "", "()V"); + } + + static class JsrHook extends ReturnHook { + ArrayList jsrList; + CodeGen cgen; + int var; + + JsrHook(CodeGen gen) { + super(gen); + jsrList = new ArrayList(); + cgen = gen; + var = -1; + } + + private int getVar(int size) { + if (var < 0) { + var = cgen.getMaxLocals(); + cgen.incMaxLocals(size); + } + + return var; + } + + private void jsrJmp(Bytecode b) { + b.addOpcode(Opcode.GOTO); + jsrList.add(new int[] {b.currentPc(), var}); + b.addIndex(0); + } + + protected boolean doit(Bytecode b, int opcode) { + switch (opcode) { + case Opcode.RETURN : + jsrJmp(b); + break; + case ARETURN : + b.addAstore(getVar(1)); + jsrJmp(b); + b.addAload(var); + break; + case IRETURN : + b.addIstore(getVar(1)); + jsrJmp(b); + b.addIload(var); + break; + case LRETURN : + b.addLstore(getVar(2)); + jsrJmp(b); + b.addLload(var); + break; + case DRETURN : + b.addDstore(getVar(2)); + jsrJmp(b); + b.addDload(var); + break; + case FRETURN : + b.addFstore(getVar(1)); + jsrJmp(b); + b.addFload(var); + break; + default : + throw new RuntimeException("fatal"); + } + + return false; + } + } + + static class JsrHook2 extends ReturnHook { + int var; + int target; + + JsrHook2(CodeGen gen, int[] retTarget) { + super(gen); + target = retTarget[0]; + var = retTarget[1]; + } + + protected boolean doit(Bytecode b, int opcode) { + switch (opcode) { + case Opcode.RETURN : + break; + case ARETURN : + b.addAstore(var); + break; + case IRETURN : + b.addIstore(var); + break; + case LRETURN : + b.addLstore(var); + break; + case DRETURN : + b.addDstore(var); + break; + case FRETURN : + b.addFstore(var); + break; + default : + throw new RuntimeException("fatal"); + } + + b.addOpcode(Opcode.GOTO); + b.addIndex(target - b.currentPc() + 3); + return true; + } + } + + protected void atTryStmnt(Stmnt st) throws CompileError { + Bytecode bc = bytecode; + Stmnt body = (Stmnt)st.getLeft(); + if (body == null) + return; + + ASTList catchList = (ASTList)st.getRight().getLeft(); + Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft(); + ArrayList gotoList = new ArrayList(); + + JsrHook jsrHook = null; + if (finallyBlock != null) + jsrHook = new JsrHook(this); + + int start = bc.currentPc(); + body.accept(this); + int end = bc.currentPc(); + if (start == end) + throw new CompileError("empty try block"); + + boolean tryNotReturn = !hasReturned; + if (tryNotReturn) { + bc.addOpcode(Opcode.GOTO); + gotoList.add(new Integer(bc.currentPc())); + bc.addIndex(0); // correct later + } + + int var = getMaxLocals(); + incMaxLocals(1); + while (catchList != null) { + // catch clause + Pair p = (Pair)catchList.head(); + catchList = catchList.tail(); + Declarator decl = (Declarator)p.getLeft(); + Stmnt block = (Stmnt)p.getRight(); + + decl.setLocalVar(var); + + CtClass type = resolver.lookupClassByJvmName(decl.getClassName()); + decl.setClassName(MemberResolver.javaToJvmName(type.getName())); + bc.addExceptionHandler(start, end, bc.currentPc(), type); + bc.growStack(1); + bc.addAstore(var); + hasReturned = false; + if (block != null) + block.accept(this); + + if (!hasReturned) { + bc.addOpcode(Opcode.GOTO); + gotoList.add(new Integer(bc.currentPc())); + bc.addIndex(0); // correct later + tryNotReturn = true; + } + } + + if (finallyBlock != null) { + jsrHook.remove(this); + // catch (any) clause + int pcAnyCatch = bc.currentPc(); + bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0); + bc.growStack(1); + bc.addAstore(var); + hasReturned = false; + finallyBlock.accept(this); + if (!hasReturned) { + bc.addAload(var); + bc.addOpcode(ATHROW); + } + + addFinally(jsrHook.jsrList, finallyBlock); + } + + int pcEnd = bc.currentPc(); + patchGoto(gotoList, pcEnd); + hasReturned = !tryNotReturn; + if (finallyBlock != null) { + if (tryNotReturn) + finallyBlock.accept(this); + } + } + + /** + * Adds a finally clause for earch return statement. + */ + private void addFinally(ArrayList returnList, Stmnt finallyBlock) + throws CompileError + { + Bytecode bc = bytecode; + int n = returnList.size(); + for (int i = 0; i < n; ++i) { + final int[] ret = (int[])returnList.get(i); + int pc = ret[0]; + bc.write16bit(pc, bc.currentPc() - pc + 1); + ReturnHook hook = new JsrHook2(this, ret); + finallyBlock.accept(this); + hook.remove(this); + if (!hasReturned) { + bc.addOpcode(Opcode.GOTO); + bc.addIndex(pc + 3 - bc.currentPc()); + } + } + } + + public void atNewExpr(NewExpr expr) throws CompileError { + if (expr.isArray()) + atNewArrayExpr(expr); + else { + CtClass clazz = resolver.lookupClassByName(expr.getClassName()); + String cname = clazz.getName(); + ASTList args = expr.getArguments(); + bytecode.addNew(cname); + bytecode.addOpcode(DUP); + + atMethodCallCore(clazz, MethodInfo.nameInit, args, + false, true, -1, null); + + exprType = CLASS; + arrayDim = 0; + className = MemberResolver.javaToJvmName(cname); + } + } + + public void atNewArrayExpr(NewExpr expr) throws CompileError { + int type = expr.getArrayType(); + ASTList size = expr.getArraySize(); + ASTList classname = expr.getClassName(); + ArrayInit init = expr.getInitializer(); + if (size.length() > 1) { + if (init != null) + throw new CompileError( + "sorry, multi-dimensional array initializer " + + "for new is not supported"); + + atMultiNewArray(type, classname, size); + return; + } + + ASTree sizeExpr = size.head(); + atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init); + } + + private void atNewArrayExpr2(int type, ASTree sizeExpr, + String jvmClassname, ArrayInit init) throws CompileError { + if (init == null) + if (sizeExpr == null) + throw new CompileError("no array size"); + else + sizeExpr.accept(this); + else + if (sizeExpr == null) { + int s = init.length(); + bytecode.addIconst(s); + } + else + throw new CompileError("unnecessary array size specified for new"); + + String elementClass; + if (type == CLASS) { + elementClass = resolveClassName(jvmClassname); + bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass)); + } + else { + elementClass = null; + int atype = 0; + switch (type) { + case BOOLEAN : + atype = T_BOOLEAN; + break; + case CHAR : + atype = T_CHAR; + break; + case FLOAT : + atype = T_FLOAT; + break; + case DOUBLE : + atype = T_DOUBLE; + break; + case BYTE : + atype = T_BYTE; + break; + case SHORT : + atype = T_SHORT; + break; + case INT : + atype = T_INT; + break; + case LONG : + atype = T_LONG; + break; + default : + badNewExpr(); + break; + } + + bytecode.addOpcode(NEWARRAY); + bytecode.add(atype); + } + + if (init != null) { + int s = init.length(); + ASTList list = init; + for (int i = 0; i < s; i++) { + bytecode.addOpcode(DUP); + bytecode.addIconst(i); + list.head().accept(this); + if (!isRefType(type)) + atNumCastExpr(exprType, type); + + bytecode.addOpcode(getArrayWriteOp(type, 0)); + list = list.tail(); + } + } + + exprType = type; + arrayDim = 1; + className = elementClass; + } + + private static void badNewExpr() throws CompileError { + throw new CompileError("bad new expression"); + } + + protected void atArrayVariableAssign(ArrayInit init, int varType, + int varArray, String varClass) throws CompileError { + atNewArrayExpr2(varType, null, varClass, init); + } + + public void atArrayInit(ArrayInit init) throws CompileError { + throw new CompileError("array initializer is not supported"); + } + + protected void atMultiNewArray(int type, ASTList classname, ASTList size) + throws CompileError + { + int count, dim; + dim = size.length(); + for (count = 0; size != null; size = size.tail()) { + ASTree s = size.head(); + if (s == null) + break; // int[][][] a = new int[3][4][]; + + ++count; + s.accept(this); + if (exprType != INT) + throw new CompileError("bad type for array size"); + } + + String desc; + exprType = type; + arrayDim = dim; + if (type == CLASS) { + className = resolveClassName(classname); + desc = toJvmArrayName(className, dim); + } + else + desc = toJvmTypeName(type, dim); + + bytecode.addMultiNewarray(desc, count); + } + + public void atCallExpr(CallExpr expr) throws CompileError { + String mname = null; + CtClass targetClass = null; + ASTree method = expr.oprand1(); + ASTList args = (ASTList)expr.oprand2(); + boolean isStatic = false; + boolean isSpecial = false; + int aload0pos = -1; + + MemberResolver.Method cached = expr.getMethod(); + if (method instanceof Member) { + mname = ((Member)method).get(); + targetClass = thisClass; + if (inStaticMethod || (cached != null && cached.isStatic())) + isStatic = true; // should be static + else { + aload0pos = bytecode.currentPc(); + bytecode.addAload(0); // this + } + } + else if (method instanceof Keyword) { // constructor + isSpecial = true; + mname = MethodInfo.nameInit; // + targetClass = thisClass; + if (inStaticMethod) + throw new CompileError("a constructor cannot be static"); + else + bytecode.addAload(0); // this + + if (((Keyword)method).get() == SUPER) + targetClass = MemberResolver.getSuperclass(targetClass); + } + else if (method instanceof Expr) { + Expr e = (Expr)method; + mname = ((Symbol)e.oprand2()).get(); + int op = e.getOperator(); + if (op == MEMBER) { // static method + targetClass + = resolver.lookupClass(((Symbol)e.oprand1()).get(), false); + isStatic = true; + } + else if (op == '.') { + ASTree target = e.oprand1(); + String classFollowedByDotSuper = TypeChecker.isDotSuper(target); + if (classFollowedByDotSuper != null) { + isSpecial = true; + targetClass = MemberResolver.getSuperInterface(thisClass, + classFollowedByDotSuper); + if (inStaticMethod || (cached != null && cached.isStatic())) + isStatic = true; // should be static + else { + aload0pos = bytecode.currentPc(); + bytecode.addAload(0); // this + } + } + else { + if (target instanceof Keyword) + if (((Keyword)target).get() == SUPER) + isSpecial = true; + + try { + target.accept(this); + } + catch (NoFieldException nfe) { + if (nfe.getExpr() != target) + throw nfe; + + // it should be a static method. + exprType = CLASS; + arrayDim = 0; + className = nfe.getField(); // JVM-internal + isStatic = true; + } + + if (arrayDim > 0) + targetClass = resolver.lookupClass(javaLangObject, true); + else if (exprType == CLASS /* && arrayDim == 0 */) + targetClass = resolver.lookupClassByJvmName(className); + else + badMethod(); + } + } + else + badMethod(); + } + else + fatal(); + + atMethodCallCore(targetClass, mname, args, isStatic, isSpecial, + aload0pos, cached); + } + + private static void badMethod() throws CompileError { + throw new CompileError("bad method"); + } + + /* + * atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew + * + * @param targetClass the class at which method lookup starts. + * @param found not null if the method look has been already done. + */ + public void atMethodCallCore(CtClass targetClass, String mname, + ASTList args, boolean isStatic, boolean isSpecial, + int aload0pos, MemberResolver.Method found) + throws CompileError + { + int nargs = getMethodArgsLength(args); + int[] types = new int[nargs]; + int[] dims = new int[nargs]; + String[] cnames = new String[nargs]; + + if (!isStatic && found != null && found.isStatic()) { + bytecode.addOpcode(POP); + isStatic = true; + } + + int stack = bytecode.getStackDepth(); + + // generate code for evaluating arguments. + atMethodArgs(args, types, dims, cnames); + + if (found == null) + found = resolver.lookupMethod(targetClass, thisClass, thisMethod, + mname, types, dims, cnames); + + if (found == null) { + String msg; + if (mname.equals(MethodInfo.nameInit)) + msg = "constructor not found"; + else + msg = "Method " + mname + " not found in " + + targetClass.getName(); + + throw new CompileError(msg); + } + + atMethodCallCore2(targetClass, mname, isStatic, isSpecial, + aload0pos, found); + } + + private void atMethodCallCore2(CtClass targetClass, String mname, + boolean isStatic, boolean isSpecial, + int aload0pos, + MemberResolver.Method found) + throws CompileError + { + CtClass declClass = found.declaring; + MethodInfo minfo = found.info; + String desc = minfo.getDescriptor(); + int acc = minfo.getAccessFlags(); + + if (mname.equals(MethodInfo.nameInit)) { + isSpecial = true; + if (declClass != targetClass) + throw new CompileError("no such constructor: " + targetClass.getName()); + + if (declClass != thisClass && AccessFlag.isPrivate(acc)) { + desc = getAccessibleConstructor(desc, declClass, minfo); + bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter + } + } + else if (AccessFlag.isPrivate(acc)) + if (declClass == thisClass) + isSpecial = true; + else { + isSpecial = false; + isStatic = true; + String origDesc = desc; + if ((acc & AccessFlag.STATIC) == 0) + desc = Descriptor.insertParameter(declClass.getName(), + origDesc); + + acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC; + mname = getAccessiblePrivate(mname, origDesc, desc, + minfo, declClass); + } + + boolean popTarget = false; + if ((acc & AccessFlag.STATIC) != 0) { + if (!isStatic) { + /* this method is static but the target object is + on stack. It must be popped out. If aload0pos >= 0, + then the target object was pushed by aload_0. It is + overwritten by NOP. + */ + isStatic = true; + if (aload0pos >= 0) + bytecode.write(aload0pos, NOP); + else + popTarget = true; + } + + bytecode.addInvokestatic(declClass, mname, desc); + } + else if (isSpecial) // if (isSpecial && notStatic(acc)) + bytecode.addInvokespecial(declClass, mname, desc); + else { + if (!Modifier.isPublic(declClass.getModifiers()) + || declClass.isInterface() != targetClass.isInterface()) + declClass = targetClass; + + if (declClass.isInterface()) { + int nargs = Descriptor.paramSize(desc) + 1; + bytecode.addInvokeinterface(declClass, mname, desc, nargs); + } + else + if (isStatic) + throw new CompileError(mname + " is not static"); + else + bytecode.addInvokevirtual(declClass, mname, desc); + } + + setReturnType(desc, isStatic, popTarget); + } + + /* + * Finds (or adds if necessary) a hidden accessor if the method + * is in an enclosing class. + * + * @param desc the descriptor of the method. + * @param declClass the class declaring the method. + */ + protected String getAccessiblePrivate(String methodName, String desc, + String newDesc, MethodInfo minfo, + CtClass declClass) + throws CompileError + { + if (isEnclosing(declClass, thisClass)) { + AccessorMaker maker = declClass.getAccessorMaker(); + if (maker != null) + return maker.getMethodAccessor(methodName, desc, newDesc, + minfo); + } + + throw new CompileError("Method " + methodName + + " is private"); + } + + /* + * Finds (or adds if necessary) a hidden constructor if the given + * constructor is in an enclosing class. + * + * @param desc the descriptor of the constructor. + * @param declClass the class declaring the constructor. + * @param minfo the method info of the constructor. + * @return the descriptor of the hidden constructor. + */ + protected String getAccessibleConstructor(String desc, CtClass declClass, + MethodInfo minfo) + throws CompileError + { + if (isEnclosing(declClass, thisClass)) { + AccessorMaker maker = declClass.getAccessorMaker(); + if (maker != null) + return maker.getConstructor(declClass, desc, minfo); + } + + throw new CompileError("the called constructor is private in " + + declClass.getName()); + } + + private boolean isEnclosing(CtClass outer, CtClass inner) { + try { + while (inner != null) { + inner = inner.getDeclaringClass(); + if (inner == outer) + return true; + } + } + catch (NotFoundException e) {} + return false; + } + + public int getMethodArgsLength(ASTList args) { + return ASTList.length(args); + } + + public void atMethodArgs(ASTList args, int[] types, int[] dims, + String[] cnames) throws CompileError { + int i = 0; + while (args != null) { + ASTree a = args.head(); + a.accept(this); + types[i] = exprType; + dims[i] = arrayDim; + cnames[i] = className; + ++i; + args = args.tail(); + } + } + + void setReturnType(String desc, boolean isStatic, boolean popTarget) + throws CompileError + { + int i = desc.indexOf(')'); + if (i < 0) + badMethod(); + + char c = desc.charAt(++i); + int dim = 0; + while (c == '[') { + ++dim; + c = desc.charAt(++i); + } + + arrayDim = dim; + if (c == 'L') { + int j = desc.indexOf(';', i + 1); + if (j < 0) + badMethod(); + + exprType = CLASS; + className = desc.substring(i + 1, j); + } + else { + exprType = MemberResolver.descToType(c); + className = null; + } + + int etype = exprType; + if (isStatic) { + if (popTarget) { + if (is2word(etype, dim)) { + bytecode.addOpcode(DUP2_X1); + bytecode.addOpcode(POP2); + bytecode.addOpcode(POP); + } + else if (etype == VOID) + bytecode.addOpcode(POP); + else { + bytecode.addOpcode(SWAP); + bytecode.addOpcode(POP); + } + } + } + } + + protected void atFieldAssign(Expr expr, int op, ASTree left, + ASTree right, boolean doDup) throws CompileError + { + CtField f = fieldAccess(left, false); + boolean is_static = resultStatic; + if (op != '=' && !is_static) + bytecode.addOpcode(DUP); + + int fi; + if (op == '=') { + FieldInfo finfo = f.getFieldInfo2(); + setFieldType(finfo); + AccessorMaker maker = isAccessibleField(f, finfo); + if (maker == null) + fi = addFieldrefInfo(f, finfo); + else + fi = 0; + } + else + fi = atFieldRead(f, is_static); + + int fType = exprType; + int fDim = arrayDim; + String cname = className; + + atAssignCore(expr, op, right, fType, fDim, cname); + + boolean is2w = is2word(fType, fDim); + if (doDup) { + int dup_code; + if (is_static) + dup_code = (is2w ? DUP2 : DUP); + else + dup_code = (is2w ? DUP2_X1 : DUP_X1); + + bytecode.addOpcode(dup_code); + } + + atFieldAssignCore(f, is_static, fi, is2w); + + exprType = fType; + arrayDim = fDim; + className = cname; + } + + /* If fi == 0, the field must be a private field in an enclosing class. + */ + private void atFieldAssignCore(CtField f, boolean is_static, int fi, + boolean is2byte) throws CompileError { + if (fi != 0) { + if (is_static) { + bytecode.add(PUTSTATIC); + bytecode.growStack(is2byte ? -2 : -1); + } + else { + bytecode.add(PUTFIELD); + bytecode.growStack(is2byte ? -3 : -2); + } + + bytecode.addIndex(fi); + } + else { + CtClass declClass = f.getDeclaringClass(); + AccessorMaker maker = declClass.getAccessorMaker(); + // make should be non null. + FieldInfo finfo = f.getFieldInfo2(); + MethodInfo minfo = maker.getFieldSetter(finfo, is_static); + bytecode.addInvokestatic(declClass, minfo.getName(), + minfo.getDescriptor()); + } + } + + /* overwritten in JvstCodeGen. + */ + public void atMember(Member mem) throws CompileError { + atFieldRead(mem); + } + + protected void atFieldRead(ASTree expr) throws CompileError + { + CtField f = fieldAccess(expr, true); + if (f == null) { + atArrayLength(expr); + return; + } + + boolean is_static = resultStatic; + ASTree cexpr = TypeChecker.getConstantFieldValue(f); + if (cexpr == null) + atFieldRead(f, is_static); + else { + cexpr.accept(this); + setFieldType(f.getFieldInfo2()); + } + } + + private void atArrayLength(ASTree expr) throws CompileError { + if (arrayDim == 0) + throw new CompileError(".length applied to a non array"); + + bytecode.addOpcode(ARRAYLENGTH); + exprType = INT; + arrayDim = 0; + } + + /** + * Generates bytecode for reading a field value. + * It returns a fieldref_info index or zero if the field is a private + * one declared in an enclosing class. + */ + private int atFieldRead(CtField f, boolean isStatic) throws CompileError { + FieldInfo finfo = f.getFieldInfo2(); + boolean is2byte = setFieldType(finfo); + AccessorMaker maker = isAccessibleField(f, finfo); + if (maker != null) { + MethodInfo minfo = maker.getFieldGetter(finfo, isStatic); + bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(), + minfo.getDescriptor()); + return 0; + } + else { + int fi = addFieldrefInfo(f, finfo); + if (isStatic) { + bytecode.add(GETSTATIC); + bytecode.growStack(is2byte ? 2 : 1); + } + else { + bytecode.add(GETFIELD); + bytecode.growStack(is2byte ? 1 : 0); + } + + bytecode.addIndex(fi); + return fi; + } + } + + /** + * Returns null if the field is accessible. Otherwise, it throws + * an exception or it returns AccessorMaker if the field is a private + * one declared in an enclosing class. + */ + private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo) + throws CompileError + { + if (AccessFlag.isPrivate(finfo.getAccessFlags()) + && f.getDeclaringClass() != thisClass) { + CtClass declClass = f.getDeclaringClass(); + if (isEnclosing(declClass, thisClass)) { + AccessorMaker maker = declClass.getAccessorMaker(); + if (maker != null) + return maker; + else + throw new CompileError("fatal error. bug?"); + } + else + throw new CompileError("Field " + f.getName() + " in " + + declClass.getName() + " is private."); + } + + return null; // accessible field + } + + /** + * Sets exprType, arrayDim, and className. + * + * @return true if the field type is long or double. + */ + private boolean setFieldType(FieldInfo finfo) throws CompileError { + String type = finfo.getDescriptor(); + + int i = 0; + int dim = 0; + char c = type.charAt(i); + while (c == '[') { + ++dim; + c = type.charAt(++i); + } + + arrayDim = dim; + exprType = MemberResolver.descToType(c); + + if (c == 'L') + className = type.substring(i + 1, type.indexOf(';', i + 1)); + else + className = null; + + boolean is2byte = dim == 0 && (c == 'J' || c == 'D'); + return is2byte; + } + + private int addFieldrefInfo(CtField f, FieldInfo finfo) { + ConstPool cp = bytecode.getConstPool(); + String cname = f.getDeclaringClass().getName(); + int ci = cp.addClassInfo(cname); + String name = finfo.getName(); + String type = finfo.getDescriptor(); + return cp.addFieldrefInfo(ci, name, type); + } + + protected void atClassObject2(String cname) throws CompileError { + if (getMajorVersion() < ClassFile.JAVA_5) + super.atClassObject2(cname); + else + bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname)); + } + + protected void atFieldPlusPlus(int token, boolean isPost, + ASTree oprand, Expr expr, boolean doDup) + throws CompileError + { + CtField f = fieldAccess(oprand, false); + boolean is_static = resultStatic; + if (!is_static) + bytecode.addOpcode(DUP); + + int fi = atFieldRead(f, is_static); + int t = exprType; + boolean is2w = is2word(t, arrayDim); + + int dup_code; + if (is_static) + dup_code = (is2w ? DUP2 : DUP); + else + dup_code = (is2w ? DUP2_X1 : DUP_X1); + + atPlusPlusCore(dup_code, doDup, token, isPost, expr); + atFieldAssignCore(f, is_static, fi, is2w); + } + + /* This method also returns a value in resultStatic. + * + * @param acceptLength true if array length is acceptable + */ + protected CtField fieldAccess(ASTree expr, boolean acceptLength) + throws CompileError + { + if (expr instanceof Member) { + String name = ((Member)expr).get(); + CtField f = null; + try { + f = thisClass.getField(name); + } + catch (NotFoundException e) { + // EXPR might be part of a static member access? + throw new NoFieldException(name, expr); + } + + boolean is_static = Modifier.isStatic(f.getModifiers()); + if (!is_static) + if (inStaticMethod) + throw new CompileError( + "not available in a static method: " + name); + else + bytecode.addAload(0); // this + + resultStatic = is_static; + return f; + } + else if (expr instanceof Expr) { + Expr e = (Expr)expr; + int op = e.getOperator(); + if (op == MEMBER) { + /* static member by # (extension by Javassist) + * For example, if int.class is parsed, the resulting tree + * is (# "java.lang.Integer" "TYPE"). + */ + CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(), + (Symbol)e.oprand2()); + resultStatic = true; + return f; + } + else if (op == '.') { + CtField f = null; + try { + e.oprand1().accept(this); + /* Don't call lookupFieldByJvmName2(). + * The left operand of . is not a class name but + * a normal expression. + */ + if (exprType == CLASS && arrayDim == 0) + f = resolver.lookupFieldByJvmName(className, + (Symbol)e.oprand2()); + else if (acceptLength && arrayDim > 0 + && ((Symbol)e.oprand2()).get().equals("length")) + return null; // expr is an array length. + else + badLvalue(); + + boolean is_static = Modifier.isStatic(f.getModifiers()); + if (is_static) + bytecode.addOpcode(POP); + + resultStatic = is_static; + return f; + } + catch (NoFieldException nfe) { + if (nfe.getExpr() != e.oprand1()) + throw nfe; + + /* EXPR should be a static field. + * If EXPR might be part of a qualified class name, + * lookupFieldByJvmName2() throws NoFieldException. + */ + Symbol fname = (Symbol)e.oprand2(); + String cname = nfe.getField(); + f = resolver.lookupFieldByJvmName2(cname, fname, expr); + resultStatic = true; + return f; + } + } + else + badLvalue(); + } + else + badLvalue(); + + resultStatic = false; + return null; // never reach + } + + private static void badLvalue() throws CompileError { + throw new CompileError("bad l-value"); + } + + public CtClass[] makeParamList(MethodDecl md) throws CompileError { + CtClass[] params; + ASTList plist = md.getParams(); + if (plist == null) + params = new CtClass[0]; + else { + int i = 0; + params = new CtClass[plist.length()]; + while (plist != null) { + params[i++] = resolver.lookupClass((Declarator)plist.head()); + plist = plist.tail(); + } + } + + return params; + } + + public CtClass[] makeThrowsList(MethodDecl md) throws CompileError { + CtClass[] clist; + ASTList list = md.getThrows(); + if (list == null) + return null; + else { + int i = 0; + clist = new CtClass[list.length()]; + while (list != null) { + clist[i++] = resolver.lookupClassByName((ASTList)list.head()); + list = list.tail(); + } + + return clist; + } + } + + /* Converts a class name into a JVM-internal representation. + * + * It may also expand a simple class name to java.lang.*. + * For example, this converts Object into java/lang/Object. + */ + protected String resolveClassName(ASTList name) throws CompileError { + return resolver.resolveClassName(name); + } + + /* Expands a simple class name to java.lang.*. + * For example, this converts Object into java/lang/Object. + */ + protected String resolveClassName(String jvmName) throws CompileError { + return resolver.resolveJvmClassName(jvmName); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/MemberResolver.java b/src/main/java/com/wenshuo/agent/javassist/compiler/MemberResolver.java new file mode 100644 index 0000000..933a626 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/MemberResolver.java @@ -0,0 +1,617 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import java.util.Hashtable; +import java.lang.ref.WeakReference; +import java.util.WeakHashMap; +import java.util.List; +import java.util.Iterator; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.ast.*; + +/* Code generator methods depending on javassist.* classes. + */ +public class MemberResolver implements TokenId { + private ClassPool classPool; + + public MemberResolver(ClassPool cp) { + classPool = cp; + } + + public ClassPool getClassPool() { return classPool; } + + private static void fatal() throws CompileError { + throw new CompileError("fatal"); + } + + public static class Method { + public CtClass declaring; + public MethodInfo info; + public int notmatch; + + public Method(CtClass c, MethodInfo i, int n) { + declaring = c; + info = i; + notmatch = n; + } + + /** + * Returns true if the invoked method is static. + */ + public boolean isStatic() { + int acc = info.getAccessFlags(); + return (acc & AccessFlag.STATIC) != 0; + } + } + + public Method lookupMethod(CtClass clazz, CtClass currentClass, MethodInfo current, + String methodName, + int[] argTypes, int[] argDims, + String[] argClassNames) + throws CompileError + { + Method maybe = null; + // to enable the creation of a recursively called method + if (current != null && clazz == currentClass) + if (current.getName().equals(methodName)) { + int res = compareSignature(current.getDescriptor(), + argTypes, argDims, argClassNames); + if (res != NO) { + Method r = new Method(clazz, current, res); + if (res == YES) + return r; + else + maybe = r; + } + } + + Method m = lookupMethod(clazz, methodName, argTypes, argDims, + argClassNames, maybe != null); + if (m != null) + return m; + else + return maybe; + } + + private Method lookupMethod(CtClass clazz, String methodName, + int[] argTypes, int[] argDims, + String[] argClassNames, boolean onlyExact) + throws CompileError + { + Method maybe = null; + ClassFile cf = clazz.getClassFile2(); + // If the class is an array type, the class file is null. + // If so, search the super class java.lang.Object for clone() etc. + if (cf != null) { + List list = cf.getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + if (minfo.getName().equals(methodName)) { + int res = compareSignature(minfo.getDescriptor(), + argTypes, argDims, argClassNames); + if (res != NO) { + Method r = new Method(clazz, minfo, res); + if (res == YES) + return r; + else if (maybe == null || maybe.notmatch > res) + maybe = r; + } + } + } + } + + if (onlyExact) + maybe = null; + else + onlyExact = maybe != null; + + int mod = clazz.getModifiers(); + boolean isIntf = Modifier.isInterface(mod); + try { + // skip searching java.lang.Object if clazz is an interface type. + if (!isIntf) { + CtClass pclazz = clazz.getSuperclass(); + if (pclazz != null) { + Method r = lookupMethod(pclazz, methodName, argTypes, + argDims, argClassNames, onlyExact); + if (r != null) + return r; + } + } + } + catch (NotFoundException e) {} + + try { + CtClass[] ifs = clazz.getInterfaces(); + int size = ifs.length; + for (int i = 0; i < size; ++i) { + Method r = lookupMethod(ifs[i], methodName, + argTypes, argDims, argClassNames, + onlyExact); + if (r != null) + return r; + } + + if (isIntf) { + // finally search java.lang.Object. + CtClass pclazz = clazz.getSuperclass(); + if (pclazz != null) { + Method r = lookupMethod(pclazz, methodName, argTypes, + argDims, argClassNames, onlyExact); + if (r != null) + return r; + } + } + } + catch (NotFoundException e) {} + + return maybe; + } + + private static final int YES = 0; + private static final int NO = -1; + + /* + * Returns YES if actual parameter types matches the given signature. + * + * argTypes, argDims, and argClassNames represent actual parameters. + * + * This method does not correctly implement the Java method dispatch + * algorithm. + * + * If some of the parameter types exactly match but others are subtypes of + * the corresponding type in the signature, this method returns the number + * of parameter types that do not exactly match. + */ + private int compareSignature(String desc, int[] argTypes, + int[] argDims, String[] argClassNames) + throws CompileError + { + int result = YES; + int i = 1; + int nArgs = argTypes.length; + if (nArgs != Descriptor.numOfParameters(desc)) + return NO; + + int len = desc.length(); + for (int n = 0; i < len; ++n) { + char c = desc.charAt(i++); + if (c == ')') + return (n == nArgs ? result : NO); + else if (n >= nArgs) + return NO; + + int dim = 0; + while (c == '[') { + ++dim; + c = desc.charAt(i++); + } + + if (argTypes[n] == NULL) { + if (dim == 0 && c != 'L') + return NO; + + if (c == 'L') + i = desc.indexOf(';', i) + 1; + } + else if (argDims[n] != dim) { + if (!(dim == 0 && c == 'L' + && desc.startsWith("java/lang/Object;", i))) + return NO; + + // if the thread reaches here, c must be 'L'. + i = desc.indexOf(';', i) + 1; + result++; + if (i <= 0) + return NO; // invalid descriptor? + } + else if (c == 'L') { // not compare + int j = desc.indexOf(';', i); + if (j < 0 || argTypes[n] != CLASS) + return NO; + + String cname = desc.substring(i, j); + if (!cname.equals(argClassNames[n])) { + CtClass clazz = lookupClassByJvmName(argClassNames[n]); + try { + if (clazz.subtypeOf(lookupClassByJvmName(cname))) + result++; + else + return NO; + } + catch (NotFoundException e) { + result++; // should be NO? + } + } + + i = j + 1; + } + else { + int t = descToType(c); + int at = argTypes[n]; + if (t != at) + if (t == INT + && (at == SHORT || at == BYTE || at == CHAR)) + result++; + else + return NO; + } + } + + return NO; + } + + /** + * Only used by fieldAccess() in MemberCodeGen and TypeChecker. + * + * @param jvmClassName a JVM class name. e.g. java/lang/String + * @see #lookupClass(String, boolean) + */ + public CtField lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym, + ASTree expr) throws NoFieldException + { + String field = fieldSym.get(); + CtClass cc = null; + try { + cc = lookupClass(jvmToJavaName(jvmClassName), true); + } + catch (CompileError e) { + // EXPR might be part of a qualified class name. + throw new NoFieldException(jvmClassName + "/" + field, expr); + } + + try { + return cc.getField(field); + } + catch (NotFoundException e) { + // maybe an inner class. + jvmClassName = javaToJvmName(cc.getName()); + throw new NoFieldException(jvmClassName + "$" + field, expr); + } + } + + /** + * @param jvmClassName a JVM class name. e.g. java/lang/String + */ + public CtField lookupFieldByJvmName(String jvmClassName, Symbol fieldName) + throws CompileError + { + return lookupField(jvmToJavaName(jvmClassName), fieldName); + } + + /** + * @param className a qualified class name. e.g. java.lang.String + */ + public CtField lookupField(String className, Symbol fieldName) + throws CompileError + { + CtClass cc = lookupClass(className, false); + try { + return cc.getField(fieldName.get()); + } + catch (NotFoundException e) {} + throw new CompileError("no such field: " + fieldName.get()); + } + + public CtClass lookupClassByName(ASTList name) throws CompileError { + return lookupClass(Declarator.astToClassName(name, '.'), false); + } + + public CtClass lookupClassByJvmName(String jvmName) throws CompileError { + return lookupClass(jvmToJavaName(jvmName), false); + } + + public CtClass lookupClass(Declarator decl) throws CompileError { + return lookupClass(decl.getType(), decl.getArrayDim(), + decl.getClassName()); + } + + /** + * @param classname jvm class name. + */ + public CtClass lookupClass(int type, int dim, String classname) + throws CompileError + { + String cname = ""; + CtClass clazz; + if (type == CLASS) { + clazz = lookupClassByJvmName(classname); + if (dim > 0) + cname = clazz.getName(); + else + return clazz; + } + else + cname = getTypeName(type); + + while (dim-- > 0) + cname += "[]"; + + return lookupClass(cname, false); + } + + /* + * type cannot be CLASS + */ + static String getTypeName(int type) throws CompileError { + String cname = ""; + switch (type) { + case BOOLEAN : + cname = "boolean"; + break; + case CHAR : + cname = "char"; + break; + case BYTE : + cname = "byte"; + break; + case SHORT : + cname = "short"; + break; + case INT : + cname = "int"; + break; + case LONG : + cname = "long"; + break; + case FLOAT : + cname = "float"; + break; + case DOUBLE : + cname = "double"; + break; + case VOID : + cname = "void"; + break; + default : + fatal(); + } + + return cname; + } + + /** + * @param name a qualified class name. e.g. java.lang.String + */ + public CtClass lookupClass(String name, boolean notCheckInner) + throws CompileError + { + Hashtable cache = getInvalidNames(); + Object found = cache.get(name); + if (found == INVALID) + throw new CompileError("no such class: " + name); + else if (found != null) + try { + return classPool.get((String)found); + } + catch (NotFoundException e) {} + + CtClass cc = null; + try { + cc = lookupClass0(name, notCheckInner); + } + catch (NotFoundException e) { + cc = searchImports(name); + } + + cache.put(name, cc.getName()); + return cc; + } + + private static final String INVALID = ""; + private static WeakHashMap invalidNamesMap = new WeakHashMap(); + private Hashtable invalidNames = null; + + // for unit tests + public static int getInvalidMapSize() { return invalidNamesMap.size(); } + + private Hashtable getInvalidNames() { + Hashtable ht = invalidNames; + if (ht == null) { + synchronized (MemberResolver.class) { + WeakReference ref = (WeakReference)invalidNamesMap.get(classPool); + if (ref != null) + ht = (Hashtable)ref.get(); + + if (ht == null) { + ht = new Hashtable(); + invalidNamesMap.put(classPool, new WeakReference(ht)); + } + } + + invalidNames = ht; + } + + return ht; + } + + private CtClass searchImports(String orgName) + throws CompileError + { + if (orgName.indexOf('.') < 0) { + Iterator it = classPool.getImportedPackages(); + while (it.hasNext()) { + String pac = (String)it.next(); + String fqName = pac + '.' + orgName; + try { + return classPool.get(fqName); + } + catch (NotFoundException e) { + try { + if (pac.endsWith("." + orgName)) + return classPool.get(pac); + } + catch (NotFoundException e2) {} + } + } + } + + getInvalidNames().put(orgName, INVALID); + throw new CompileError("no such class: " + orgName); + } + + private CtClass lookupClass0(String classname, boolean notCheckInner) + throws NotFoundException + { + CtClass cc = null; + do { + try { + cc = classPool.get(classname); + } + catch (NotFoundException e) { + int i = classname.lastIndexOf('.'); + if (notCheckInner || i < 0) + throw e; + else { + StringBuffer sbuf = new StringBuffer(classname); + sbuf.setCharAt(i, '$'); + classname = sbuf.toString(); + } + } + } while (cc == null); + return cc; + } + + /* Converts a class name into a JVM-internal representation. + * + * It may also expand a simple class name to java.lang.*. + * For example, this converts Object into java/lang/Object. + */ + public String resolveClassName(ASTList name) throws CompileError { + if (name == null) + return null; + else + return javaToJvmName(lookupClassByName(name).getName()); + } + + /* Expands a simple class name to java.lang.*. + * For example, this converts Object into java/lang/Object. + */ + public String resolveJvmClassName(String jvmName) throws CompileError { + if (jvmName == null) + return null; + else + return javaToJvmName(lookupClassByJvmName(jvmName).getName()); + } + + public static CtClass getSuperclass(CtClass c) throws CompileError { + try { + CtClass sc = c.getSuperclass(); + if (sc != null) + return sc; + } + catch (NotFoundException e) {} + throw new CompileError("cannot find the super class of " + + c.getName()); + } + + public static CtClass getSuperInterface(CtClass c, String interfaceName) + throws CompileError + { + try { + CtClass[] intfs = c.getInterfaces(); + for (int i = 0; i < intfs.length; i++) + if (intfs[i].getName().equals(interfaceName)) + return intfs[i]; + } catch (NotFoundException e) {} + throw new CompileError("cannot find the super inetrface " + interfaceName + + " of " + c.getName()); + } + + public static String javaToJvmName(String classname) { + return classname.replace('.', '/'); + } + + public static String jvmToJavaName(String classname) { + return classname.replace('/', '.'); + } + + public static int descToType(char c) throws CompileError { + switch (c) { + case 'Z' : + return BOOLEAN; + case 'C' : + return CHAR; + case 'B' : + return BYTE; + case 'S' : + return SHORT; + case 'I' : + return INT; + case 'J' : + return LONG; + case 'F' : + return FLOAT; + case 'D' : + return DOUBLE; + case 'V' : + return VOID; + case 'L' : + case '[' : + return CLASS; + default : + fatal(); + return VOID; // never reach here + } + } + + public static int getModifiers(ASTList mods) { + int m = 0; + while (mods != null) { + Keyword k = (Keyword)mods.head(); + mods = mods.tail(); + switch (k.get()) { + case STATIC : + m |= Modifier.STATIC; + break; + case FINAL : + m |= Modifier.FINAL; + break; + case SYNCHRONIZED : + m |= Modifier.SYNCHRONIZED; + break; + case ABSTRACT : + m |= Modifier.ABSTRACT; + break; + case PUBLIC : + m |= Modifier.PUBLIC; + break; + case PROTECTED : + m |= Modifier.PROTECTED; + break; + case PRIVATE : + m |= Modifier.PRIVATE; + break; + case VOLATILE : + m |= Modifier.VOLATILE; + break; + case TRANSIENT : + m |= Modifier.TRANSIENT; + break; + case STRICT : + m |= Modifier.STRICT; + break; + } + } + + return m; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/NoFieldException.java b/src/main/java/com/wenshuo/agent/javassist/compiler/NoFieldException.java new file mode 100644 index 0000000..5b35107 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/NoFieldException.java @@ -0,0 +1,40 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.compiler.ast.ASTree; + +public class NoFieldException extends CompileError { + private String fieldName; + private ASTree expr; + + /* NAME must be JVM-internal representation. + */ + public NoFieldException(String name, ASTree e) { + super("no such field: " + name); + fieldName = name; + expr = e; + } + + /* The returned name should be JVM-internal representation. + */ + public String getField() { return fieldName; } + + /* Returns the expression where this exception is thrown. + */ + public ASTree getExpr() { return expr; } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/Parser.java b/src/main/java/com/wenshuo/agent/javassist/compiler/Parser.java new file mode 100644 index 0000000..4e15650 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/Parser.java @@ -0,0 +1,1345 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.compiler.ast.*; + +public final class Parser implements TokenId { + private Lex lex; + + public Parser(Lex lex) { + this.lex = lex; + } + + public boolean hasMore() { return lex.lookAhead() >= 0; } + + /* member.declaration + * : method.declaration | field.declaration + */ + public ASTList parseMember(SymbolTable tbl) throws CompileError { + ASTList mem = parseMember1(tbl); + if (mem instanceof MethodDecl) + return parseMethod2(tbl, (MethodDecl)mem); + else + return mem; + } + + /* A method body is not parsed. + */ + public ASTList parseMember1(SymbolTable tbl) throws CompileError { + ASTList mods = parseMemberMods(); + Declarator d; + boolean isConstructor = false; + if (lex.lookAhead() == Identifier && lex.lookAhead(1) == '(') { + d = new Declarator(VOID, 0); + isConstructor = true; + } + else + d = parseFormalType(tbl); + + if (lex.get() != Identifier) + throw new SyntaxError(lex); + + String name; + if (isConstructor) + name = MethodDecl.initName; + else + name = lex.getString(); + + d.setVariable(new Symbol(name)); + if (isConstructor || lex.lookAhead() == '(') + return parseMethod1(tbl, isConstructor, mods, d); + else + return parseField(tbl, mods, d); + } + + /* field.declaration + * : member.modifiers + * formal.type Identifier + * [ "=" expression ] ";" + */ + private FieldDecl parseField(SymbolTable tbl, ASTList mods, + Declarator d) throws CompileError + { + ASTree expr = null; + if (lex.lookAhead() == '=') { + lex.get(); + expr = parseExpression(tbl); + } + + int c = lex.get(); + if (c == ';') + return new FieldDecl(mods, new ASTList(d, new ASTList(expr))); + else if (c == ',') + throw new CompileError( + "only one field can be declared in one declaration", lex); + else + throw new SyntaxError(lex); + } + + /* method.declaration + * : member.modifiers + * [ formal.type ] + * Identifier "(" [ formal.parameter ( "," formal.parameter )* ] ")" + * array.dimension + * [ THROWS class.type ( "," class.type ) ] + * ( block.statement | ";" ) + * + * Note that a method body is not parsed. + */ + private MethodDecl parseMethod1(SymbolTable tbl, boolean isConstructor, + ASTList mods, Declarator d) + throws CompileError + { + if (lex.get() != '(') + throw new SyntaxError(lex); + + ASTList parms = null; + if (lex.lookAhead() != ')') + while (true) { + parms = ASTList.append(parms, parseFormalParam(tbl)); + int t = lex.lookAhead(); + if (t == ',') + lex.get(); + else if (t == ')') + break; + } + + lex.get(); // ')' + d.addArrayDim(parseArrayDimension()); + if (isConstructor && d.getArrayDim() > 0) + throw new SyntaxError(lex); + + ASTList throwsList = null; + if (lex.lookAhead() == THROWS) { + lex.get(); + while (true) { + throwsList = ASTList.append(throwsList, parseClassType(tbl)); + if (lex.lookAhead() == ',') + lex.get(); + else + break; + } + } + + return new MethodDecl(mods, new ASTList(d, + ASTList.make(parms, throwsList, null))); + } + + /* Parses a method body. + */ + public MethodDecl parseMethod2(SymbolTable tbl, MethodDecl md) + throws CompileError + { + Stmnt body = null; + if (lex.lookAhead() == ';') + lex.get(); + else { + body = parseBlock(tbl); + if (body == null) + body = new Stmnt(BLOCK); + } + + md.sublist(4).setHead(body); + return md; + } + + /* member.modifiers + * : ( FINAL | SYNCHRONIZED | ABSTRACT + * | PUBLIC | PROTECTED | PRIVATE | STATIC + * | VOLATILE | TRANSIENT | STRICT )* + */ + private ASTList parseMemberMods() { + int t; + ASTList list = null; + while (true) { + t = lex.lookAhead(); + if (t == ABSTRACT || t == FINAL || t == PUBLIC || t == PROTECTED + || t == PRIVATE || t == SYNCHRONIZED || t == STATIC + || t == VOLATILE || t == TRANSIENT || t == STRICT) + list = new ASTList(new Keyword(lex.get()), list); + else + break; + } + + return list; + } + + /* formal.type : ( build-in-type | class.type ) array.dimension + */ + private Declarator parseFormalType(SymbolTable tbl) throws CompileError { + int t = lex.lookAhead(); + if (isBuiltinType(t) || t == VOID) { + lex.get(); // primitive type + int dim = parseArrayDimension(); + return new Declarator(t, dim); + } + else { + ASTList name = parseClassType(tbl); + int dim = parseArrayDimension(); + return new Declarator(name, dim); + } + } + + private static boolean isBuiltinType(int t) { + return (t == BOOLEAN || t == BYTE || t == CHAR || t == SHORT + || t == INT || t == LONG || t == FLOAT || t == DOUBLE); + } + + /* formal.parameter : formal.type Identifier array.dimension + */ + private Declarator parseFormalParam(SymbolTable tbl) + throws CompileError + { + Declarator d = parseFormalType(tbl); + if (lex.get() != Identifier) + throw new SyntaxError(lex); + + String name = lex.getString(); + d.setVariable(new Symbol(name)); + d.addArrayDim(parseArrayDimension()); + tbl.append(name, d); + return d; + } + + /* statement : [ label ":" ]* labeled.statement + * + * labeled.statement + * : block.statement + * | if.statement + * | while.statement + * | do.statement + * | for.statement + * | switch.statement + * | try.statement + * | return.statement + * | thorw.statement + * | break.statement + * | continue.statement + * | declaration.or.expression + * | ";" + * + * This method may return null (empty statement). + */ + public Stmnt parseStatement(SymbolTable tbl) + throws CompileError + { + int t = lex.lookAhead(); + if (t == '{') + return parseBlock(tbl); + else if (t == ';') { + lex.get(); + return new Stmnt(BLOCK); // empty statement + } + else if (t == Identifier && lex.lookAhead(1) == ':') { + lex.get(); // Identifier + String label = lex.getString(); + lex.get(); // ':' + return Stmnt.make(LABEL, new Symbol(label), parseStatement(tbl)); + } + else if (t == IF) + return parseIf(tbl); + else if (t == WHILE) + return parseWhile(tbl); + else if (t == DO) + return parseDo(tbl); + else if (t == FOR) + return parseFor(tbl); + else if (t == TRY) + return parseTry(tbl); + else if (t == SWITCH) + return parseSwitch(tbl); + else if (t == SYNCHRONIZED) + return parseSynchronized(tbl); + else if (t == RETURN) + return parseReturn(tbl); + else if (t == THROW) + return parseThrow(tbl); + else if (t == BREAK) + return parseBreak(tbl); + else if (t == CONTINUE) + return parseContinue(tbl); + else + return parseDeclarationOrExpression(tbl, false); + } + + /* block.statement : "{" statement* "}" + */ + private Stmnt parseBlock(SymbolTable tbl) throws CompileError { + if (lex.get() != '{') + throw new SyntaxError(lex); + + Stmnt body = null; + SymbolTable tbl2 = new SymbolTable(tbl); + while (lex.lookAhead() != '}') { + Stmnt s = parseStatement(tbl2); + if (s != null) + body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s)); + } + + lex.get(); // '}' + if (body == null) + return new Stmnt(BLOCK); // empty block + else + return body; + } + + /* if.statement : IF "(" expression ")" statement + * [ ELSE statement ] + */ + private Stmnt parseIf(SymbolTable tbl) throws CompileError { + int t = lex.get(); // IF + ASTree expr = parseParExpression(tbl); + Stmnt thenp = parseStatement(tbl); + Stmnt elsep; + if (lex.lookAhead() == ELSE) { + lex.get(); + elsep = parseStatement(tbl); + } + else + elsep = null; + + return new Stmnt(t, expr, new ASTList(thenp, new ASTList(elsep))); + } + + /* while.statement : WHILE "(" expression ")" statement + */ + private Stmnt parseWhile(SymbolTable tbl) + throws CompileError + { + int t = lex.get(); // WHILE + ASTree expr = parseParExpression(tbl); + Stmnt body = parseStatement(tbl); + return new Stmnt(t, expr, body); + } + + /* do.statement : DO statement WHILE "(" expression ")" ";" + */ + private Stmnt parseDo(SymbolTable tbl) throws CompileError { + int t = lex.get(); // DO + Stmnt body = parseStatement(tbl); + if (lex.get() != WHILE || lex.get() != '(') + throw new SyntaxError(lex); + + ASTree expr = parseExpression(tbl); + if (lex.get() != ')' || lex.get() != ';') + throw new SyntaxError(lex); + + return new Stmnt(t, expr, body); + } + + /* for.statement : FOR "(" decl.or.expr expression ";" expression ")" + * statement + */ + private Stmnt parseFor(SymbolTable tbl) throws CompileError { + Stmnt expr1, expr3; + ASTree expr2; + int t = lex.get(); // FOR + + SymbolTable tbl2 = new SymbolTable(tbl); + + if (lex.get() != '(') + throw new SyntaxError(lex); + + if (lex.lookAhead() == ';') { + lex.get(); + expr1 = null; + } + else + expr1 = parseDeclarationOrExpression(tbl2, true); + + if (lex.lookAhead() == ';') + expr2 = null; + else + expr2 = parseExpression(tbl2); + + if (lex.get() != ';') + throw new CompileError("; is missing", lex); + + if (lex.lookAhead() == ')') + expr3 = null; + else + expr3 = parseExprList(tbl2); + + if (lex.get() != ')') + throw new CompileError(") is missing", lex); + + Stmnt body = parseStatement(tbl2); + return new Stmnt(t, expr1, new ASTList(expr2, + new ASTList(expr3, body))); + } + + /* switch.statement : SWITCH "(" expression ")" "{" switch.block "}" + * + * swtich.block : ( switch.label statement* )* + * + * swtich.label : DEFAULT ":" + * | CASE const.expression ":" + */ + private Stmnt parseSwitch(SymbolTable tbl) throws CompileError { + int t = lex.get(); // SWITCH + ASTree expr = parseParExpression(tbl); + Stmnt body = parseSwitchBlock(tbl); + return new Stmnt(t, expr, body); + } + + private Stmnt parseSwitchBlock(SymbolTable tbl) throws CompileError { + if (lex.get() != '{') + throw new SyntaxError(lex); + + SymbolTable tbl2 = new SymbolTable(tbl); + Stmnt s = parseStmntOrCase(tbl2); + if (s == null) + throw new CompileError("empty switch block", lex); + + int op = s.getOperator(); + if (op != CASE && op != DEFAULT) + throw new CompileError("no case or default in a switch block", + lex); + + Stmnt body = new Stmnt(BLOCK, s); + while (lex.lookAhead() != '}') { + Stmnt s2 = parseStmntOrCase(tbl2); + if (s2 != null) { + int op2 = s2.getOperator(); + if (op2 == CASE || op2 == DEFAULT) { + body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s2)); + s = s2; + } + else + s = (Stmnt)ASTList.concat(s, new Stmnt(BLOCK, s2)); + } + } + + lex.get(); // '}' + return body; + } + + private Stmnt parseStmntOrCase(SymbolTable tbl) throws CompileError { + int t = lex.lookAhead(); + if (t != CASE && t != DEFAULT) + return parseStatement(tbl); + + lex.get(); + Stmnt s; + if (t == CASE) + s = new Stmnt(t, parseExpression(tbl)); + else + s = new Stmnt(DEFAULT); + + if (lex.get() != ':') + throw new CompileError(": is missing", lex); + + return s; + } + + /* synchronized.statement : + * SYNCHRONIZED "(" expression ")" block.statement + */ + private Stmnt parseSynchronized(SymbolTable tbl) throws CompileError { + int t = lex.get(); // SYNCHRONIZED + if (lex.get() != '(') + throw new SyntaxError(lex); + + ASTree expr = parseExpression(tbl); + if (lex.get() != ')') + throw new SyntaxError(lex); + + Stmnt body = parseBlock(tbl); + return new Stmnt(t, expr, body); + } + + /* try.statement + * : TRY block.statement + * [ CATCH "(" class.type Identifier ")" block.statement ]* + * [ FINALLY block.statement ]* + */ + private Stmnt parseTry(SymbolTable tbl) throws CompileError { + lex.get(); // TRY + Stmnt block = parseBlock(tbl); + ASTList catchList = null; + while (lex.lookAhead() == CATCH) { + lex.get(); // CATCH + if (lex.get() != '(') + throw new SyntaxError(lex); + + SymbolTable tbl2 = new SymbolTable(tbl); + Declarator d = parseFormalParam(tbl2); + if (d.getArrayDim() > 0 || d.getType() != CLASS) + throw new SyntaxError(lex); + + if (lex.get() != ')') + throw new SyntaxError(lex); + + Stmnt b = parseBlock(tbl2); + catchList = ASTList.append(catchList, new Pair(d, b)); + } + + Stmnt finallyBlock = null; + if (lex.lookAhead() == FINALLY) { + lex.get(); // FINALLY + finallyBlock = parseBlock(tbl); + } + + return Stmnt.make(TRY, block, catchList, finallyBlock); + } + + /* return.statement : RETURN [ expression ] ";" + */ + private Stmnt parseReturn(SymbolTable tbl) throws CompileError { + int t = lex.get(); // RETURN + Stmnt s = new Stmnt(t); + if (lex.lookAhead() != ';') + s.setLeft(parseExpression(tbl)); + + if (lex.get() != ';') + throw new CompileError("; is missing", lex); + + return s; + } + + /* throw.statement : THROW expression ";" + */ + private Stmnt parseThrow(SymbolTable tbl) throws CompileError { + int t = lex.get(); // THROW + ASTree expr = parseExpression(tbl); + if (lex.get() != ';') + throw new CompileError("; is missing", lex); + + return new Stmnt(t, expr); + } + + /* break.statement : BREAK [ Identifier ] ";" + */ + private Stmnt parseBreak(SymbolTable tbl) + throws CompileError + { + return parseContinue(tbl); + } + + /* continue.statement : CONTINUE [ Identifier ] ";" + */ + private Stmnt parseContinue(SymbolTable tbl) + throws CompileError + { + int t = lex.get(); // CONTINUE + Stmnt s = new Stmnt(t); + int t2 = lex.get(); + if (t2 == Identifier) { + s.setLeft(new Symbol(lex.getString())); + t2 = lex.get(); + } + + if (t2 != ';') + throw new CompileError("; is missing", lex); + + return s; + } + + /* declaration.or.expression + * : [ FINAL ] built-in-type array.dimension declarators + * | [ FINAL ] class.type array.dimension declarators + * | expression ';' + * | expr.list ';' if exprList is true + * + * Note: FINAL is currently ignored. This must be fixed + * in future. + */ + private Stmnt parseDeclarationOrExpression(SymbolTable tbl, + boolean exprList) + throws CompileError + { + int t = lex.lookAhead(); + while (t == FINAL) { + lex.get(); + t = lex.lookAhead(); + } + + if (isBuiltinType(t)) { + t = lex.get(); + int dim = parseArrayDimension(); + return parseDeclarators(tbl, new Declarator(t, dim)); + } + else if (t == Identifier) { + int i = nextIsClassType(0); + if (i >= 0) + if (lex.lookAhead(i) == Identifier) { + ASTList name = parseClassType(tbl); + int dim = parseArrayDimension(); + return parseDeclarators(tbl, new Declarator(name, dim)); + } + } + + Stmnt expr; + if (exprList) + expr = parseExprList(tbl); + else + expr = new Stmnt(EXPR, parseExpression(tbl)); + + if (lex.get() != ';') + throw new CompileError("; is missing", lex); + + return expr; + } + + /* expr.list : ( expression ',')* expression + */ + private Stmnt parseExprList(SymbolTable tbl) throws CompileError { + Stmnt expr = null; + for (;;) { + Stmnt e = new Stmnt(EXPR, parseExpression(tbl)); + expr = (Stmnt)ASTList.concat(expr, new Stmnt(BLOCK, e)); + if (lex.lookAhead() == ',') + lex.get(); + else + return expr; + } + } + + /* declarators : declarator [ ',' declarator ]* ';' + */ + private Stmnt parseDeclarators(SymbolTable tbl, Declarator d) + throws CompileError + { + Stmnt decl = null; + for (;;) { + decl = (Stmnt)ASTList.concat(decl, + new Stmnt(DECL, parseDeclarator(tbl, d))); + int t = lex.get(); + if (t == ';') + return decl; + else if (t != ',') + throw new CompileError("; is missing", lex); + } + } + + /* declarator : Identifier array.dimension [ '=' initializer ] + */ + private Declarator parseDeclarator(SymbolTable tbl, Declarator d) + throws CompileError + { + if (lex.get() != Identifier || d.getType() == VOID) + throw new SyntaxError(lex); + + String name = lex.getString(); + Symbol symbol = new Symbol(name); + int dim = parseArrayDimension(); + ASTree init = null; + if (lex.lookAhead() == '=') { + lex.get(); + init = parseInitializer(tbl); + } + + Declarator decl = d.make(symbol, dim, init); + tbl.append(name, decl); + return decl; + } + + /* initializer : expression | array.initializer + */ + private ASTree parseInitializer(SymbolTable tbl) throws CompileError { + if (lex.lookAhead() == '{') + return parseArrayInitializer(tbl); + else + return parseExpression(tbl); + } + + /* array.initializer : + * '{' (( array.initializer | expression ) ',')* '}' + */ + private ArrayInit parseArrayInitializer(SymbolTable tbl) + throws CompileError + { + lex.get(); // '{' + ASTree expr = parseExpression(tbl); + ArrayInit init = new ArrayInit(expr); + while (lex.lookAhead() == ',') { + lex.get(); + expr = parseExpression(tbl); + ASTList.append(init, expr); + } + + if (lex.get() != '}') + throw new SyntaxError(lex); + + return init; + } + + /* par.expression : '(' expression ')' + */ + private ASTree parseParExpression(SymbolTable tbl) throws CompileError { + if (lex.get() != '(') + throw new SyntaxError(lex); + + ASTree expr = parseExpression(tbl); + if (lex.get() != ')') + throw new SyntaxError(lex); + + return expr; + } + + /* expression : conditional.expr + * | conditional.expr assign.op expression (right-to-left) + */ + public ASTree parseExpression(SymbolTable tbl) throws CompileError { + ASTree left = parseConditionalExpr(tbl); + if (!isAssignOp(lex.lookAhead())) + return left; + + int t = lex.get(); + ASTree right = parseExpression(tbl); + return AssignExpr.makeAssign(t, left, right); + } + + private static boolean isAssignOp(int t) { + return t == '=' || t == MOD_E || t == AND_E + || t == MUL_E || t == PLUS_E || t == MINUS_E || t == DIV_E + || t == EXOR_E || t == OR_E || t == LSHIFT_E + || t == RSHIFT_E || t == ARSHIFT_E; + } + + /* conditional.expr (right-to-left) + * : logical.or.expr [ '?' expression ':' conditional.expr ] + */ + private ASTree parseConditionalExpr(SymbolTable tbl) throws CompileError { + ASTree cond = parseBinaryExpr(tbl); + if (lex.lookAhead() == '?') { + lex.get(); + ASTree thenExpr = parseExpression(tbl); + if (lex.get() != ':') + throw new CompileError(": is missing", lex); + + ASTree elseExpr = parseExpression(tbl); + return new CondExpr(cond, thenExpr, elseExpr); + } + else + return cond; + } + + /* logical.or.expr 10 (operator precedence) + * : logical.and.expr + * | logical.or.expr OROR logical.and.expr left-to-right + * + * logical.and.expr 9 + * : inclusive.or.expr + * | logical.and.expr ANDAND inclusive.or.expr + * + * inclusive.or.expr 8 + * : exclusive.or.expr + * | inclusive.or.expr "|" exclusive.or.expr + * + * exclusive.or.expr 7 + * : and.expr + * | exclusive.or.expr "^" and.expr + * + * and.expr 6 + * : equality.expr + * | and.expr "&" equality.expr + * + * equality.expr 5 + * : relational.expr + * | equality.expr (EQ | NEQ) relational.expr + * + * relational.expr 4 + * : shift.expr + * | relational.expr (LE | GE | "<" | ">") shift.expr + * | relational.expr INSTANCEOF class.type ("[" "]")* + * + * shift.expr 3 + * : additive.expr + * | shift.expr (LSHIFT | RSHIFT | ARSHIFT) additive.expr + * + * additive.expr 2 + * : multiply.expr + * | additive.expr ("+" | "-") multiply.expr + * + * multiply.expr 1 + * : unary.expr + * | multiply.expr ("*" | "/" | "%") unary.expr + */ + private ASTree parseBinaryExpr(SymbolTable tbl) throws CompileError { + ASTree expr = parseUnaryExpr(tbl); + for (;;) { + int t = lex.lookAhead(); + int p = getOpPrecedence(t); + if (p == 0) + return expr; + else + expr = binaryExpr2(tbl, expr, p); + } + } + + private ASTree parseInstanceOf(SymbolTable tbl, ASTree expr) + throws CompileError + { + int t = lex.lookAhead(); + if (isBuiltinType(t)) { + lex.get(); // primitive type + int dim = parseArrayDimension(); + return new InstanceOfExpr(t, dim, expr); + } + else { + ASTList name = parseClassType(tbl); + int dim = parseArrayDimension(); + return new InstanceOfExpr(name, dim, expr); + } + } + + private ASTree binaryExpr2(SymbolTable tbl, ASTree expr, int prec) + throws CompileError + { + int t = lex.get(); + if (t == INSTANCEOF) + return parseInstanceOf(tbl, expr); + + ASTree expr2 = parseUnaryExpr(tbl); + for (;;) { + int t2 = lex.lookAhead(); + int p2 = getOpPrecedence(t2); + if (p2 != 0 && prec > p2) + expr2 = binaryExpr2(tbl, expr2, p2); + else + return BinExpr.makeBin(t, expr, expr2); + } + } + + // !"#$%&'( )*+,-./0 12345678 9:;<=>? + private static final int[] binaryOpPrecedence + = { 0, 0, 0, 0, 1, 6, 0, 0, + 0, 1, 2, 0, 2, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 4, 0 }; + + private int getOpPrecedence(int c) { + if ('!' <= c && c <= '?') + return binaryOpPrecedence[c - '!']; + else if (c == '^') + return 7; + else if (c == '|') + return 8; + else if (c == ANDAND) + return 9; + else if (c == OROR) + return 10; + else if (c == EQ || c == NEQ) + return 5; + else if (c == LE || c == GE || c == INSTANCEOF) + return 4; + else if (c == LSHIFT || c == RSHIFT || c == ARSHIFT) + return 3; + else + return 0; // not a binary operator + } + + /* unary.expr : "++"|"--" unary.expr + | "+"|"-" unary.expr + | "!"|"~" unary.expr + | cast.expr + | postfix.expr + + unary.expr.not.plus.minus is a unary expression starting without + "+", "-", "++", or "--". + */ + private ASTree parseUnaryExpr(SymbolTable tbl) throws CompileError { + int t; + switch (lex.lookAhead()) { + case '+' : + case '-' : + case PLUSPLUS : + case MINUSMINUS : + case '!' : + case '~' : + t = lex.get(); + if (t == '-') { + int t2 = lex.lookAhead(); + switch (t2) { + case LongConstant : + case IntConstant : + case CharConstant : + lex.get(); + return new IntConst(-lex.getLong(), t2); + case DoubleConstant : + case FloatConstant : + lex.get(); + return new DoubleConst(-lex.getDouble(), t2); + default : + break; + } + } + + return Expr.make(t, parseUnaryExpr(tbl)); + case '(' : + return parseCast(tbl); + default : + return parsePostfix(tbl); + } + } + + /* cast.expr : "(" builtin.type ("[" "]")* ")" unary.expr + | "(" class.type ("[" "]")* ")" unary.expr2 + + unary.expr2 is a unary.expr beginning with "(", NULL, StringL, + Identifier, THIS, SUPER, or NEW. + + Either "(int.class)" or "(String[].class)" is a not cast expression. + */ + private ASTree parseCast(SymbolTable tbl) throws CompileError { + int t = lex.lookAhead(1); + if (isBuiltinType(t) && nextIsBuiltinCast()) { + lex.get(); // '(' + lex.get(); // primitive type + int dim = parseArrayDimension(); + if (lex.get() != ')') + throw new CompileError(") is missing", lex); + + return new CastExpr(t, dim, parseUnaryExpr(tbl)); + } + else if (t == Identifier && nextIsClassCast()) { + lex.get(); // '(' + ASTList name = parseClassType(tbl); + int dim = parseArrayDimension(); + if (lex.get() != ')') + throw new CompileError(") is missing", lex); + + return new CastExpr(name, dim, parseUnaryExpr(tbl)); + } + else + return parsePostfix(tbl); + } + + private boolean nextIsBuiltinCast() { + int t; + int i = 2; + while ((t = lex.lookAhead(i++)) == '[') + if (lex.lookAhead(i++) != ']') + return false; + + return lex.lookAhead(i - 1) == ')'; + } + + private boolean nextIsClassCast() { + int i = nextIsClassType(1); + if (i < 0) + return false; + + int t = lex.lookAhead(i); + if (t != ')') + return false; + + t = lex.lookAhead(i + 1); + return t == '(' || t == NULL || t == StringL + || t == Identifier || t == THIS || t == SUPER || t == NEW + || t == TRUE || t == FALSE || t == LongConstant + || t == IntConstant || t == CharConstant + || t == DoubleConstant || t == FloatConstant; + } + + private int nextIsClassType(int i) { + int t; + while (lex.lookAhead(++i) == '.') + if (lex.lookAhead(++i) != Identifier) + return -1; + + while ((t = lex.lookAhead(i++)) == '[') + if (lex.lookAhead(i++) != ']') + return -1; + + return i - 1; + } + + /* array.dimension : [ "[" "]" ]* + */ + private int parseArrayDimension() throws CompileError { + int arrayDim = 0; + while (lex.lookAhead() == '[') { + ++arrayDim; + lex.get(); + if (lex.get() != ']') + throw new CompileError("] is missing", lex); + } + + return arrayDim; + } + + /* class.type : Identifier ( "." Identifier )* + */ + private ASTList parseClassType(SymbolTable tbl) throws CompileError { + ASTList list = null; + for (;;) { + if (lex.get() != Identifier) + throw new SyntaxError(lex); + + list = ASTList.append(list, new Symbol(lex.getString())); + if (lex.lookAhead() == '.') + lex.get(); + else + break; + } + + return list; + } + + /* postfix.expr : number.literal + * | primary.expr + * | method.expr + * | postfix.expr "++" | "--" + * | postfix.expr "[" array.size "]" + * | postfix.expr "." Identifier + * | postfix.expr ( "[" "]" )* "." CLASS + * | postfix.expr "#" Identifier + * | postfix.expr "." SUPER + * + * "#" is not an operator of regular Java. It separates + * a class name and a member name in an expression for static member + * access. For example, + * java.lang.Integer.toString(3) in regular Java + * can be written like this: + * java.lang.Integer#toString(3) for this compiler. + */ + private ASTree parsePostfix(SymbolTable tbl) throws CompileError { + int token = lex.lookAhead(); + switch (token) { // see also parseUnaryExpr() + case LongConstant : + case IntConstant : + case CharConstant : + lex.get(); + return new IntConst(lex.getLong(), token); + case DoubleConstant : + case FloatConstant : + lex.get(); + return new DoubleConst(lex.getDouble(), token); + default : + break; + } + + String str; + ASTree index; + ASTree expr = parsePrimaryExpr(tbl); + int t; + while (true) { + switch (lex.lookAhead()) { + case '(' : + expr = parseMethodCall(tbl, expr); + break; + case '[' : + if (lex.lookAhead(1) == ']') { + int dim = parseArrayDimension(); + if (lex.get() != '.' || lex.get() != CLASS) + throw new SyntaxError(lex); + + expr = parseDotClass(expr, dim); + } + else { + index = parseArrayIndex(tbl); + if (index == null) + throw new SyntaxError(lex); + + expr = Expr.make(ARRAY, expr, index); + } + break; + case PLUSPLUS : + case MINUSMINUS : + t = lex.get(); + expr = Expr.make(t, null, expr); + break; + case '.' : + lex.get(); + t = lex.get(); + if (t == CLASS) + expr = parseDotClass(expr, 0); + else if (t == SUPER) + expr = Expr.make('.', new Symbol(toClassName(expr)), new Keyword(t)); + else if (t == Identifier) { + str = lex.getString(); + expr = Expr.make('.', expr, new Member(str)); + } + else + throw new CompileError("missing member name", lex); + break; + case '#' : + lex.get(); + t = lex.get(); + if (t != Identifier) + throw new CompileError("missing static member name", lex); + + str = lex.getString(); + expr = Expr.make(MEMBER, new Symbol(toClassName(expr)), + new Member(str)); + break; + default : + return expr; + } + } + } + + /* Parse a .class expression on a class type. For example, + * String.class => ('.' "String" "class") + * String[].class => ('.' "[LString;" "class") + */ + private ASTree parseDotClass(ASTree className, int dim) + throws CompileError + { + String cname = toClassName(className); + if (dim > 0) { + StringBuffer sbuf = new StringBuffer(); + while (dim-- > 0) + sbuf.append('['); + + sbuf.append('L').append(cname.replace('.', '/')).append(';'); + cname = sbuf.toString(); + } + + return Expr.make('.', new Symbol(cname), new Member("class")); + } + + /* Parses a .class expression on a built-in type. For example, + * int.class => ('#' "java.lang.Integer" "TYPE") + * int[].class => ('.' "[I", "class") + */ + private ASTree parseDotClass(int builtinType, int dim) + throws CompileError + { + if (dim > 0) { + String cname = CodeGen.toJvmTypeName(builtinType, dim); + return Expr.make('.', new Symbol(cname), new Member("class")); + } + else { + String cname; + switch(builtinType) { + case BOOLEAN : + cname = "java.lang.Boolean"; + break; + case BYTE : + cname = "java.lang.Byte"; + break; + case CHAR : + cname = "java.lang.Character"; + break; + case SHORT : + cname = "java.lang.Short"; + break; + case INT : + cname = "java.lang.Integer"; + break; + case LONG : + cname = "java.lang.Long"; + break; + case FLOAT : + cname = "java.lang.Float"; + break; + case DOUBLE : + cname = "java.lang.Double"; + break; + case VOID : + cname = "java.lang.Void"; + break; + default : + throw new CompileError("invalid builtin type: " + + builtinType); + } + + return Expr.make(MEMBER, new Symbol(cname), new Member("TYPE")); + } + } + + /* method.call : method.expr "(" argument.list ")" + * method.expr : THIS | SUPER | Identifier + * | postfix.expr "." Identifier + * | postfix.expr "#" Identifier + */ + private ASTree parseMethodCall(SymbolTable tbl, ASTree expr) + throws CompileError + { + if (expr instanceof Keyword) { + int token = ((Keyword)expr).get(); + if (token != THIS && token != SUPER) + throw new SyntaxError(lex); + } + else if (expr instanceof Symbol) // Identifier + ; + else if (expr instanceof Expr) { + int op = ((Expr)expr).getOperator(); + if (op != '.' && op != MEMBER) + throw new SyntaxError(lex); + } + + return CallExpr.makeCall(expr, parseArgumentList(tbl)); + } + + private String toClassName(ASTree name) + throws CompileError + { + StringBuffer sbuf = new StringBuffer(); + toClassName(name, sbuf); + return sbuf.toString(); + } + + private void toClassName(ASTree name, StringBuffer sbuf) + throws CompileError + { + if (name instanceof Symbol) { + sbuf.append(((Symbol)name).get()); + return; + } + else if (name instanceof Expr) { + Expr expr = (Expr)name; + if (expr.getOperator() == '.') { + toClassName(expr.oprand1(), sbuf); + sbuf.append('.'); + toClassName(expr.oprand2(), sbuf); + return; + } + } + + throw new CompileError("bad static member access", lex); + } + + /* primary.expr : THIS | SUPER | TRUE | FALSE | NULL + * | StringL + * | Identifier + * | NEW new.expr + * | "(" expression ")" + * | builtin.type ( "[" "]" )* "." CLASS + * + * Identifier represents either a local variable name, a member name, + * or a class name. + */ + private ASTree parsePrimaryExpr(SymbolTable tbl) throws CompileError { + int t; + String name; + Declarator decl; + ASTree expr; + + switch (t = lex.get()) { + case THIS : + case SUPER : + case TRUE : + case FALSE : + case NULL : + return new Keyword(t); + case Identifier : + name = lex.getString(); + decl = tbl.lookup(name); + if (decl == null) + return new Member(name); // this or static member + else + return new Variable(name, decl); // local variable + case StringL : + return new StringL(lex.getString()); + case NEW : + return parseNew(tbl); + case '(' : + expr = parseExpression(tbl); + if (lex.get() == ')') + return expr; + else + throw new CompileError(") is missing", lex); + default : + if (isBuiltinType(t) || t == VOID) { + int dim = parseArrayDimension(); + if (lex.get() == '.' && lex.get() == CLASS) + return parseDotClass(t, dim); + } + + throw new SyntaxError(lex); + } + } + + /* new.expr : class.type "(" argument.list ")" + * | class.type array.size [ array.initializer ] + * | primitive.type array.size [ array.initializer ] + */ + private NewExpr parseNew(SymbolTable tbl) throws CompileError { + ArrayInit init = null; + int t = lex.lookAhead(); + if (isBuiltinType(t)) { + lex.get(); + ASTList size = parseArraySize(tbl); + if (lex.lookAhead() == '{') + init = parseArrayInitializer(tbl); + + return new NewExpr(t, size, init); + } + else if (t == Identifier) { + ASTList name = parseClassType(tbl); + t = lex.lookAhead(); + if (t == '(') { + ASTList args = parseArgumentList(tbl); + return new NewExpr(name, args); + } + else if (t == '[') { + ASTList size = parseArraySize(tbl); + if (lex.lookAhead() == '{') + init = parseArrayInitializer(tbl); + + return NewExpr.makeObjectArray(name, size, init); + } + } + + throw new SyntaxError(lex); + } + + /* array.size : [ array.index ]* + */ + private ASTList parseArraySize(SymbolTable tbl) throws CompileError { + ASTList list = null; + while (lex.lookAhead() == '[') + list = ASTList.append(list, parseArrayIndex(tbl)); + + return list; + } + + /* array.index : "[" [ expression ] "]" + */ + private ASTree parseArrayIndex(SymbolTable tbl) throws CompileError { + lex.get(); // '[' + if (lex.lookAhead() == ']') { + lex.get(); + return null; + } + else { + ASTree index = parseExpression(tbl); + if (lex.get() != ']') + throw new CompileError("] is missing", lex); + + return index; + } + } + + /* argument.list : "(" [ expression [ "," expression ]* ] ")" + */ + private ASTList parseArgumentList(SymbolTable tbl) throws CompileError { + if (lex.get() != '(') + throw new CompileError("( is missing", lex); + + ASTList list = null; + if (lex.lookAhead() != ')') + for (;;) { + list = ASTList.append(list, parseExpression(tbl)); + if (lex.lookAhead() == ',') + lex.get(); + else + break; + } + + if (lex.get() != ')') + throw new CompileError(") is missing", lex); + + return list; + } +} + diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ProceedHandler.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ProceedHandler.java new file mode 100644 index 0000000..7cb7ef4 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ProceedHandler.java @@ -0,0 +1,30 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.bytecode.Bytecode; +import com.wenshuo.agent.javassist.compiler.ast.ASTList; + +/** + * An interface to an object for implementing $proceed(). + * + * @see javassist.compiler.JvstCodeGen#setProceedHandler(ProceedHandler, String) + */ +public interface ProceedHandler { + void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError; + void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError; +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/SymbolTable.java b/src/main/java/com/wenshuo/agent/javassist/compiler/SymbolTable.java new file mode 100644 index 0000000..1a3edc0 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/SymbolTable.java @@ -0,0 +1,45 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import java.util.HashMap; +import com.wenshuo.agent.javassist.compiler.ast.Declarator; + +public final class SymbolTable extends HashMap { + private SymbolTable parent; + + public SymbolTable() { this(null); } + + public SymbolTable(SymbolTable p) { + super(); + parent = p; + } + + public SymbolTable getParent() { return parent; } + + public Declarator lookup(String name) { + Declarator found = (Declarator)get(name); + if (found == null && parent != null) + return parent.lookup(name); + else + return found; + } + + public void append(String name, Declarator value) { + put(name, value); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/SyntaxError.java b/src/main/java/com/wenshuo/agent/javassist/compiler/SyntaxError.java new file mode 100644 index 0000000..5a6f4b4 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/SyntaxError.java @@ -0,0 +1,23 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +public class SyntaxError extends CompileError { + public SyntaxError(Lex lexer) { + super("syntax error near \"" + lexer.getTextAround() + "\"", lexer); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/TokenId.java b/src/main/java/com/wenshuo/agent/javassist/compiler/TokenId.java new file mode 100644 index 0000000..58538a6 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/TokenId.java @@ -0,0 +1,125 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +public interface TokenId { + int ABSTRACT = 300; + int BOOLEAN = 301; + int BREAK = 302; + int BYTE = 303; + int CASE = 304; + int CATCH = 305; + int CHAR = 306; + int CLASS = 307; + int CONST = 308; // reserved keyword + int CONTINUE = 309; + int DEFAULT = 310; + int DO = 311; + int DOUBLE = 312; + int ELSE = 313; + int EXTENDS = 314; + int FINAL = 315; + int FINALLY = 316; + int FLOAT = 317; + int FOR = 318; + int GOTO = 319; // reserved keyword + int IF = 320; + int IMPLEMENTS = 321; + int IMPORT = 322; + int INSTANCEOF = 323; + int INT = 324; + int INTERFACE = 325; + int LONG = 326; + int NATIVE = 327; + int NEW = 328; + int PACKAGE = 329; + int PRIVATE = 330; + int PROTECTED = 331; + int PUBLIC = 332; + int RETURN = 333; + int SHORT = 334; + int STATIC = 335; + int SUPER = 336; + int SWITCH = 337; + int SYNCHRONIZED = 338; + int THIS = 339; + int THROW = 340; + int THROWS = 341; + int TRANSIENT = 342; + int TRY = 343; + int VOID = 344; + int VOLATILE = 345; + int WHILE = 346; + int STRICT = 347; + + int NEQ = 350; // != + int MOD_E = 351; // %= + int AND_E = 352; // &= + int MUL_E = 353; // *= + int PLUS_E = 354; // += + int MINUS_E = 355; // -= + int DIV_E = 356; // /= + int LE = 357; // <= + int EQ = 358; // == + int GE = 359; // >= + int EXOR_E = 360; // ^= + int OR_E = 361; // |= + int PLUSPLUS = 362; // ++ + int MINUSMINUS = 363; // -- + int LSHIFT = 364; // << + int LSHIFT_E = 365; // <<= + int RSHIFT = 366; // >> + int RSHIFT_E = 367; // >>= + int OROR = 368; // || + int ANDAND = 369; // && + int ARSHIFT = 370; // >>> + int ARSHIFT_E = 371; // >>>= + + // operators from NEQ to ARSHIFT_E + String opNames[] = { "!=", "%=", "&=", "*=", "+=", "-=", "/=", + "<=", "==", ">=", "^=", "|=", "++", "--", + "<<", "<<=", ">>", ">>=", "||", "&&", ">>>", + ">>>=" }; + + // operators from MOD_E to ARSHIFT_E + int assignOps[] = { '%', '&', '*', '+', '-', '/', 0, 0, 0, + '^', '|', 0, 0, 0, LSHIFT, 0, RSHIFT, 0, 0, 0, + ARSHIFT }; + + int Identifier = 400; + int CharConstant = 401; + int IntConstant = 402; + int LongConstant = 403; + int FloatConstant = 404; + int DoubleConstant = 405; + int StringL = 406; + + int TRUE = 410; + int FALSE = 411; + int NULL = 412; + + int CALL = 'C'; // method call + int ARRAY = 'A'; // array access + int MEMBER = '#'; // static member access + + int EXPR = 'E'; // expression statement + int LABEL = 'L'; // label statement + int BLOCK = 'B'; // block statement + int DECL = 'D'; // declaration statement + + int BadToken = 500; +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/TypeChecker.java b/src/main/java/com/wenshuo/agent/javassist/compiler/TypeChecker.java new file mode 100644 index 0000000..80153d0 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/TypeChecker.java @@ -0,0 +1,1044 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtField; +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.Modifier; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.compiler.ast.*; +import com.wenshuo.agent.javassist.bytecode.*; + +public class TypeChecker extends Visitor implements Opcode, TokenId { + static final String javaLangObject = "java.lang.Object"; + static final String jvmJavaLangObject = "java/lang/Object"; + static final String jvmJavaLangString = "java/lang/String"; + static final String jvmJavaLangClass = "java/lang/Class"; + + /* The following fields are used by atXXX() methods + * for returning the type of the compiled expression. + */ + protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ... + protected int arrayDim; + protected String className; // JVM-internal representation + + protected MemberResolver resolver; + protected CtClass thisClass; + protected MethodInfo thisMethod; + + public TypeChecker(CtClass cc, ClassPool cp) { + resolver = new MemberResolver(cp); + thisClass = cc; + thisMethod = null; + } + + /* + * Converts an array of tuples of exprType, arrayDim, and className + * into a String object. + */ + protected static String argTypesToString(int[] types, int[] dims, + String[] cnames) { + StringBuffer sbuf = new StringBuffer(); + sbuf.append('('); + int n = types.length; + if (n > 0) { + int i = 0; + while (true) { + typeToString(sbuf, types[i], dims[i], cnames[i]); + if (++i < n) + sbuf.append(','); + else + break; + } + } + + sbuf.append(')'); + return sbuf.toString(); + } + + /* + * Converts a tuple of exprType, arrayDim, and className + * into a String object. + */ + protected static StringBuffer typeToString(StringBuffer sbuf, + int type, int dim, String cname) { + String s; + if (type == CLASS) + s = MemberResolver.jvmToJavaName(cname); + else if (type == NULL) + s = "Object"; + else + try { + s = MemberResolver.getTypeName(type); + } + catch (CompileError e) { + s = "?"; + } + + sbuf.append(s); + while (dim-- > 0) + sbuf.append("[]"); + + return sbuf; + } + + /** + * Records the currently compiled method. + */ + public void setThisMethod(MethodInfo m) { + thisMethod = m; + } + + protected static void fatal() throws CompileError { + throw new CompileError("fatal"); + } + + /** + * Returns the JVM-internal representation of this class name. + */ + protected String getThisName() { + return MemberResolver.javaToJvmName(thisClass.getName()); + } + + /** + * Returns the JVM-internal representation of this super class name. + */ + protected String getSuperName() throws CompileError { + return MemberResolver.javaToJvmName( + MemberResolver.getSuperclass(thisClass).getName()); + } + + /* Converts a class name into a JVM-internal representation. + * + * It may also expand a simple class name to java.lang.*. + * For example, this converts Object into java/lang/Object. + */ + protected String resolveClassName(ASTList name) throws CompileError { + return resolver.resolveClassName(name); + } + + /* Expands a simple class name to java.lang.*. + * For example, this converts Object into java/lang/Object. + */ + protected String resolveClassName(String jvmName) throws CompileError { + return resolver.resolveJvmClassName(jvmName); + } + + public void atNewExpr(NewExpr expr) throws CompileError { + if (expr.isArray()) + atNewArrayExpr(expr); + else { + CtClass clazz = resolver.lookupClassByName(expr.getClassName()); + String cname = clazz.getName(); + ASTList args = expr.getArguments(); + atMethodCallCore(clazz, MethodInfo.nameInit, args); + exprType = CLASS; + arrayDim = 0; + className = MemberResolver.javaToJvmName(cname); + } + } + + public void atNewArrayExpr(NewExpr expr) throws CompileError { + int type = expr.getArrayType(); + ASTList size = expr.getArraySize(); + ASTList classname = expr.getClassName(); + ASTree init = expr.getInitializer(); + if (init != null) + init.accept(this); + + if (size.length() > 1) + atMultiNewArray(type, classname, size); + else { + ASTree sizeExpr = size.head(); + if (sizeExpr != null) + sizeExpr.accept(this); + + exprType = type; + arrayDim = 1; + if (type == CLASS) + className = resolveClassName(classname); + else + className = null; + } + } + + public void atArrayInit(ArrayInit init) throws CompileError { + ASTList list = init; + while (list != null) { + ASTree h = list.head(); + list = list.tail(); + if (h != null) + h.accept(this); + } + } + + protected void atMultiNewArray(int type, ASTList classname, ASTList size) + throws CompileError + { + int count, dim; + dim = size.length(); + for (count = 0; size != null; size = size.tail()) { + ASTree s = size.head(); + if (s == null) + break; // int[][][] a = new int[3][4][]; + + ++count; + s.accept(this); + } + + exprType = type; + arrayDim = dim; + if (type == CLASS) + className = resolveClassName(classname); + else + className = null; + } + + public void atAssignExpr(AssignExpr expr) throws CompileError { + // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>= + int op = expr.getOperator(); + ASTree left = expr.oprand1(); + ASTree right = expr.oprand2(); + if (left instanceof Variable) + atVariableAssign(expr, op, (Variable)left, + ((Variable)left).getDeclarator(), + right); + else { + if (left instanceof Expr) { + Expr e = (Expr)left; + if (e.getOperator() == ARRAY) { + atArrayAssign(expr, op, (Expr)left, right); + return; + } + } + + atFieldAssign(expr, op, left, right); + } + } + + /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. + * + * expr and var can be null. + */ + private void atVariableAssign(Expr expr, int op, Variable var, + Declarator d, ASTree right) + throws CompileError + { + int varType = d.getType(); + int varArray = d.getArrayDim(); + String varClass = d.getClassName(); + + if (op != '=') + atVariable(var); + + right.accept(this); + exprType = varType; + arrayDim = varArray; + className = varClass; + } + + private void atArrayAssign(Expr expr, int op, Expr array, + ASTree right) throws CompileError + { + atArrayRead(array.oprand1(), array.oprand2()); + int aType = exprType; + int aDim = arrayDim; + String cname = className; + right.accept(this); + exprType = aType; + arrayDim = aDim; + className = cname; + } + + protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) + throws CompileError + { + CtField f = fieldAccess(left); + atFieldRead(f); + int fType = exprType; + int fDim = arrayDim; + String cname = className; + right.accept(this); + exprType = fType; + arrayDim = fDim; + className = cname; + } + + public void atCondExpr(CondExpr expr) throws CompileError { + booleanExpr(expr.condExpr()); + expr.thenExpr().accept(this); + int type1 = exprType; + int dim1 = arrayDim; + String cname1 = className; + expr.elseExpr().accept(this); + + if (dim1 == 0 && dim1 == arrayDim) + if (CodeGen.rightIsStrong(type1, exprType)) + expr.setThen(new CastExpr(exprType, 0, expr.thenExpr())); + else if (CodeGen.rightIsStrong(exprType, type1)) { + expr.setElse(new CastExpr(type1, 0, expr.elseExpr())); + exprType = type1; + } + } + + /* + * If atBinExpr() substitutes a new expression for the original + * binary-operator expression, it changes the operator name to '+' + * (if the original is not '+') and sets the new expression to the + * left-hand-side expression and null to the right-hand-side expression. + */ + public void atBinExpr(BinExpr expr) throws CompileError { + int token = expr.getOperator(); + int k = CodeGen.lookupBinOp(token); + if (k >= 0) { + /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>> + */ + if (token == '+') { + Expr e = atPlusExpr(expr); + if (e != null) { + /* String concatenation has been translated into + * an expression using StringBuffer. + */ + e = CallExpr.makeCall(Expr.make('.', e, + new Member("toString")), null); + expr.setOprand1(e); + expr.setOprand2(null); // <---- look at this! + className = jvmJavaLangString; + } + } + else { + ASTree left = expr.oprand1(); + ASTree right = expr.oprand2(); + left.accept(this); + int type1 = exprType; + right.accept(this); + if (!isConstant(expr, token, left, right)) + computeBinExprType(expr, token, type1); + } + } + else { + /* equation: &&, ||, ==, !=, <=, >=, <, > + */ + booleanExpr(expr); + } + } + + /* EXPR must be a + expression. + * atPlusExpr() returns non-null if the given expression is string + * concatenation. The returned value is "new StringBuffer().append..". + */ + private Expr atPlusExpr(BinExpr expr) throws CompileError { + ASTree left = expr.oprand1(); + ASTree right = expr.oprand2(); + if (right == null) { + // this expression has been already type-checked. + // see atBinExpr() above. + left.accept(this); + return null; + } + + if (isPlusExpr(left)) { + Expr newExpr = atPlusExpr((BinExpr)left); + if (newExpr != null) { + right.accept(this); + exprType = CLASS; + arrayDim = 0; + className = "java/lang/StringBuffer"; + return makeAppendCall(newExpr, right); + } + } + else + left.accept(this); + + int type1 = exprType; + int dim1 = arrayDim; + String cname = className; + right.accept(this); + + if (isConstant(expr, '+', left, right)) + return null; + + if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname)) + || (exprType == CLASS && arrayDim == 0 + && jvmJavaLangString.equals(className))) { + ASTList sbufClass = ASTList.make(new Symbol("java"), + new Symbol("lang"), new Symbol("StringBuffer")); + ASTree e = new NewExpr(sbufClass, null); + exprType = CLASS; + arrayDim = 0; + className = "java/lang/StringBuffer"; + return makeAppendCall(makeAppendCall(e, left), right); + } + else { + computeBinExprType(expr, '+', type1); + return null; + } + } + + private boolean isConstant(BinExpr expr, int op, ASTree left, + ASTree right) throws CompileError + { + left = stripPlusExpr(left); + right = stripPlusExpr(right); + ASTree newExpr = null; + if (left instanceof StringL && right instanceof StringL && op == '+') + newExpr = new StringL(((StringL)left).get() + + ((StringL)right).get()); + else if (left instanceof IntConst) + newExpr = ((IntConst)left).compute(op, right); + else if (left instanceof DoubleConst) + newExpr = ((DoubleConst)left).compute(op, right); + + if (newExpr == null) + return false; // not a constant expression + else { + expr.setOperator('+'); + expr.setOprand1(newExpr); + expr.setOprand2(null); + newExpr.accept(this); // for setting exprType, arrayDim, ... + return true; + } + } + + /* CodeGen.atSwitchStmnt() also calls stripPlusExpr(). + */ + static ASTree stripPlusExpr(ASTree expr) { + if (expr instanceof BinExpr) { + BinExpr e = (BinExpr)expr; + if (e.getOperator() == '+' && e.oprand2() == null) + return e.getLeft(); + } + else if (expr instanceof Expr) { // note: BinExpr extends Expr. + Expr e = (Expr)expr; + int op = e.getOperator(); + if (op == MEMBER) { + ASTree cexpr = getConstantFieldValue((Member)e.oprand2()); + if (cexpr != null) + return cexpr; + } + else if (op == '+' && e.getRight() == null) + return e.getLeft(); + } + else if (expr instanceof Member) { + ASTree cexpr = getConstantFieldValue((Member)expr); + if (cexpr != null) + return cexpr; + } + + return expr; + } + + /** + * If MEM is a static final field, this method returns a constant + * expression representing the value of that field. + */ + private static ASTree getConstantFieldValue(Member mem) { + return getConstantFieldValue(mem.getField()); + } + + public static ASTree getConstantFieldValue(CtField f) { + if (f == null) + return null; + + Object value = f.getConstantValue(); + if (value == null) + return null; + + if (value instanceof String) + return new StringL((String)value); + else if (value instanceof Double || value instanceof Float) { + int token = (value instanceof Double) + ? DoubleConstant : FloatConstant; + return new DoubleConst(((Number)value).doubleValue(), token); + } + else if (value instanceof Number) { + int token = (value instanceof Long) ? LongConstant : IntConstant; + return new IntConst(((Number)value).longValue(), token); + } + else if (value instanceof Boolean) + return new Keyword(((Boolean)value).booleanValue() + ? TokenId.TRUE : TokenId.FALSE); + else + return null; + } + + private static boolean isPlusExpr(ASTree expr) { + if (expr instanceof BinExpr) { + BinExpr bexpr = (BinExpr)expr; + int token = bexpr.getOperator(); + return token == '+'; + } + + return false; + } + + private static Expr makeAppendCall(ASTree target, ASTree arg) { + return CallExpr.makeCall(Expr.make('.', target, new Member("append")), + new ASTList(arg)); + } + + private void computeBinExprType(BinExpr expr, int token, int type1) + throws CompileError + { + // arrayDim should be 0. + int type2 = exprType; + if (token == LSHIFT || token == RSHIFT || token == ARSHIFT) + exprType = type1; + else + insertCast(expr, type1, type2); + + if (CodeGen.isP_INT(exprType)) + exprType = INT; // type1 may be BYTE, ... + } + + private void booleanExpr(ASTree expr) + throws CompileError + { + int op = CodeGen.getCompOperator(expr); + if (op == EQ) { // ==, !=, ... + BinExpr bexpr = (BinExpr)expr; + bexpr.oprand1().accept(this); + int type1 = exprType; + int dim1 = arrayDim; + bexpr.oprand2().accept(this); + if (dim1 == 0 && arrayDim == 0) + insertCast(bexpr, type1, exprType); + } + else if (op == '!') + ((Expr)expr).oprand1().accept(this); + else if (op == ANDAND || op == OROR) { + BinExpr bexpr = (BinExpr)expr; + bexpr.oprand1().accept(this); + bexpr.oprand2().accept(this); + } + else // others + expr.accept(this); + + exprType = BOOLEAN; + arrayDim = 0; + } + + private void insertCast(BinExpr expr, int type1, int type2) + throws CompileError + { + if (CodeGen.rightIsStrong(type1, type2)) + expr.setLeft(new CastExpr(type2, 0, expr.oprand1())); + else + exprType = type1; + } + + public void atCastExpr(CastExpr expr) throws CompileError { + String cname = resolveClassName(expr.getClassName()); + expr.getOprand().accept(this); + exprType = expr.getType(); + arrayDim = expr.getArrayDim(); + className = cname; + } + + public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { + expr.getOprand().accept(this); + exprType = BOOLEAN; + arrayDim = 0; + } + + public void atExpr(Expr expr) throws CompileError { + // array access, member access, + // (unary) +, (unary) -, ++, --, !, ~ + + int token = expr.getOperator(); + ASTree oprand = expr.oprand1(); + if (token == '.') { + String member = ((Symbol)expr.oprand2()).get(); + if (member.equals("length")) + try { + atArrayLength(expr); + } + catch (NoFieldException nfe) { + // length might be a class or package name. + atFieldRead(expr); + } + else if (member.equals("class")) + atClassObject(expr); // .class + else + atFieldRead(expr); + } + else if (token == MEMBER) { // field read + String member = ((Symbol)expr.oprand2()).get(); + if (member.equals("class")) + atClassObject(expr); // .class + else + atFieldRead(expr); + } + else if (token == ARRAY) + atArrayRead(oprand, expr.oprand2()); + else if (token == PLUSPLUS || token == MINUSMINUS) + atPlusPlus(token, oprand, expr); + else if (token == '!') + booleanExpr(expr); + else if (token == CALL) // method call + fatal(); + else { + oprand.accept(this); + if (!isConstant(expr, token, oprand)) + if (token == '-' || token == '~') + if (CodeGen.isP_INT(exprType)) + exprType = INT; // type may be BYTE, ... + } + } + + private boolean isConstant(Expr expr, int op, ASTree oprand) { + oprand = stripPlusExpr(oprand); + if (oprand instanceof IntConst) { + IntConst c = (IntConst)oprand; + long v = c.get(); + if (op == '-') + v = -v; + else if (op == '~') + v = ~v; + else + return false; + + c.set(v); + } + else if (oprand instanceof DoubleConst) { + DoubleConst c = (DoubleConst)oprand; + if (op == '-') + c.set(-c.get()); + else + return false; + } + else + return false; + + expr.setOperator('+'); + return true; + } + + public void atCallExpr(CallExpr expr) throws CompileError { + String mname = null; + CtClass targetClass = null; + ASTree method = expr.oprand1(); + ASTList args = (ASTList)expr.oprand2(); + + if (method instanceof Member) { + mname = ((Member)method).get(); + targetClass = thisClass; + } + else if (method instanceof Keyword) { // constructor + mname = MethodInfo.nameInit; // + if (((Keyword)method).get() == SUPER) + targetClass = MemberResolver.getSuperclass(thisClass); + else + targetClass = thisClass; + } + else if (method instanceof Expr) { + Expr e = (Expr)method; + mname = ((Symbol)e.oprand2()).get(); + int op = e.getOperator(); + if (op == MEMBER) // static method + targetClass + = resolver.lookupClass(((Symbol)e.oprand1()).get(), + false); + else if (op == '.') { + ASTree target = e.oprand1(); + String classFollowedByDotSuper = isDotSuper(target); + if (classFollowedByDotSuper != null) + targetClass = MemberResolver.getSuperInterface(thisClass, + classFollowedByDotSuper); + else { + try { + target.accept(this); + } + catch (NoFieldException nfe) { + if (nfe.getExpr() != target) + throw nfe; + + // it should be a static method. + exprType = CLASS; + arrayDim = 0; + className = nfe.getField(); // JVM-internal + e.setOperator(MEMBER); + e.setOprand1(new Symbol(MemberResolver.jvmToJavaName( + className))); + } + + if (arrayDim > 0) + targetClass = resolver.lookupClass(javaLangObject, true); + else if (exprType == CLASS /* && arrayDim == 0 */) + targetClass = resolver.lookupClassByJvmName(className); + else + badMethod(); + } + } + else + badMethod(); + } + else + fatal(); + + MemberResolver.Method minfo + = atMethodCallCore(targetClass, mname, args); + expr.setMethod(minfo); + } + + private static void badMethod() throws CompileError { + throw new CompileError("bad method"); + } + + /** + * Returns non-null if target is something like Foo.super + * for accessing the default method in an interface. + * Otherwise, null. + * + * @return the class name followed by {@code .super} or null. + */ + static String isDotSuper(ASTree target) { + if (target instanceof Expr) { + Expr e = (Expr)target; + if (e.getOperator() == '.') { + ASTree right = e.oprand2(); + if (right instanceof Keyword && ((Keyword)right).get() == SUPER) + return ((Symbol)e.oprand1()).get(); + } + } + + return null; + } + + /** + * @return a pair of the class declaring the invoked method + * and the MethodInfo of that method. Never null. + */ + public MemberResolver.Method atMethodCallCore(CtClass targetClass, + String mname, ASTList args) + throws CompileError + { + int nargs = getMethodArgsLength(args); + int[] types = new int[nargs]; + int[] dims = new int[nargs]; + String[] cnames = new String[nargs]; + atMethodArgs(args, types, dims, cnames); + + MemberResolver.Method found + = resolver.lookupMethod(targetClass, thisClass, thisMethod, + mname, types, dims, cnames); + if (found == null) { + String clazz = targetClass.getName(); + String signature = argTypesToString(types, dims, cnames); + String msg; + if (mname.equals(MethodInfo.nameInit)) + msg = "cannot find constructor " + clazz + signature; + else + msg = mname + signature + " not found in " + clazz; + + throw new CompileError(msg); + } + + String desc = found.info.getDescriptor(); + setReturnType(desc); + return found; + } + + public int getMethodArgsLength(ASTList args) { + return ASTList.length(args); + } + + public void atMethodArgs(ASTList args, int[] types, int[] dims, + String[] cnames) throws CompileError { + int i = 0; + while (args != null) { + ASTree a = args.head(); + a.accept(this); + types[i] = exprType; + dims[i] = arrayDim; + cnames[i] = className; + ++i; + args = args.tail(); + } + } + + void setReturnType(String desc) throws CompileError { + int i = desc.indexOf(')'); + if (i < 0) + badMethod(); + + char c = desc.charAt(++i); + int dim = 0; + while (c == '[') { + ++dim; + c = desc.charAt(++i); + } + + arrayDim = dim; + if (c == 'L') { + int j = desc.indexOf(';', i + 1); + if (j < 0) + badMethod(); + + exprType = CLASS; + className = desc.substring(i + 1, j); + } + else { + exprType = MemberResolver.descToType(c); + className = null; + } + } + + private void atFieldRead(ASTree expr) throws CompileError { + atFieldRead(fieldAccess(expr)); + } + + private void atFieldRead(CtField f) throws CompileError { + FieldInfo finfo = f.getFieldInfo2(); + String type = finfo.getDescriptor(); + + int i = 0; + int dim = 0; + char c = type.charAt(i); + while (c == '[') { + ++dim; + c = type.charAt(++i); + } + + arrayDim = dim; + exprType = MemberResolver.descToType(c); + + if (c == 'L') + className = type.substring(i + 1, type.indexOf(';', i + 1)); + else + className = null; + } + + /* if EXPR is to access a static field, fieldAccess() translates EXPR + * into an expression using '#' (MEMBER). For example, it translates + * java.lang.Integer.TYPE into java.lang.Integer#TYPE. This translation + * speeds up type resolution by MemberCodeGen. + */ + protected CtField fieldAccess(ASTree expr) throws CompileError { + if (expr instanceof Member) { + Member mem = (Member)expr; + String name = mem.get(); + try { + CtField f = thisClass.getField(name); + if (Modifier.isStatic(f.getModifiers())) + mem.setField(f); + + return f; + } + catch (NotFoundException e) { + // EXPR might be part of a static member access? + throw new NoFieldException(name, expr); + } + } + else if (expr instanceof Expr) { + Expr e = (Expr)expr; + int op = e.getOperator(); + if (op == MEMBER) { + Member mem = (Member)e.oprand2(); + CtField f + = resolver.lookupField(((Symbol)e.oprand1()).get(), mem); + mem.setField(f); + return f; + } + else if (op == '.') { + try { + e.oprand1().accept(this); + } + catch (NoFieldException nfe) { + if (nfe.getExpr() != e.oprand1()) + throw nfe; + + /* EXPR should be a static field. + * If EXPR might be part of a qualified class name, + * lookupFieldByJvmName2() throws NoFieldException. + */ + return fieldAccess2(e, nfe.getField()); + } + + CompileError err = null; + try { + if (exprType == CLASS && arrayDim == 0) + return resolver.lookupFieldByJvmName(className, + (Symbol)e.oprand2()); + } + catch (CompileError ce) { + err = ce; + } + + /* If a filed name is the same name as a package's, + * a static member of a class in that package is not + * visible. For example, + * + * class Foo { + * int javassist; + * } + * + * It is impossible to add the following method: + * + * String m() { return javassist.CtClass.intType.toString(); } + * + * because javassist is a field name. However, this is + * often inconvenient, this compiler allows it. The following + * code is for that. + */ + ASTree oprnd1 = e.oprand1(); + if (oprnd1 instanceof Symbol) + return fieldAccess2(e, ((Symbol)oprnd1).get()); + + if (err != null) + throw err; + } + } + + throw new CompileError("bad filed access"); + } + + private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError { + Member fname = (Member)e.oprand2(); + CtField f = resolver.lookupFieldByJvmName2(jvmClassName, fname, e); + e.setOperator(MEMBER); + e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(jvmClassName))); + fname.setField(f); + return f; + } + + public void atClassObject(Expr expr) throws CompileError { + exprType = CLASS; + arrayDim = 0; + className =jvmJavaLangClass; + } + + public void atArrayLength(Expr expr) throws CompileError { + expr.oprand1().accept(this); + if (arrayDim == 0) + throw new NoFieldException("length", expr); + + exprType = INT; + arrayDim = 0; + } + + public void atArrayRead(ASTree array, ASTree index) + throws CompileError + { + array.accept(this); + int type = exprType; + int dim = arrayDim; + String cname = className; + index.accept(this); + exprType = type; + arrayDim = dim - 1; + className = cname; + } + + private void atPlusPlus(int token, ASTree oprand, Expr expr) + throws CompileError + { + boolean isPost = oprand == null; // ++i or i++? + if (isPost) + oprand = expr.oprand2(); + + if (oprand instanceof Variable) { + Declarator d = ((Variable)oprand).getDeclarator(); + exprType = d.getType(); + arrayDim = d.getArrayDim(); + } + else { + if (oprand instanceof Expr) { + Expr e = (Expr)oprand; + if (e.getOperator() == ARRAY) { + atArrayRead(e.oprand1(), e.oprand2()); + // arrayDim should be 0. + int t = exprType; + if (t == INT || t == BYTE || t == CHAR || t == SHORT) + exprType = INT; + + return; + } + } + + atFieldPlusPlus(oprand); + } + } + + protected void atFieldPlusPlus(ASTree oprand) throws CompileError + { + CtField f = fieldAccess(oprand); + atFieldRead(f); + int t = exprType; + if (t == INT || t == BYTE || t == CHAR || t == SHORT) + exprType = INT; + } + + public void atMember(Member mem) throws CompileError { + atFieldRead(mem); + } + + public void atVariable(Variable v) throws CompileError { + Declarator d = v.getDeclarator(); + exprType = d.getType(); + arrayDim = d.getArrayDim(); + className = d.getClassName(); + } + + public void atKeyword(Keyword k) throws CompileError { + arrayDim = 0; + int token = k.get(); + switch (token) { + case TRUE : + case FALSE : + exprType = BOOLEAN; + break; + case NULL : + exprType = NULL; + break; + case THIS : + case SUPER : + exprType = CLASS; + if (token == THIS) + className = getThisName(); + else + className = getSuperName(); + break; + default : + fatal(); + } + } + + public void atStringL(StringL s) throws CompileError { + exprType = CLASS; + arrayDim = 0; + className = jvmJavaLangString; + } + + public void atIntConst(IntConst i) throws CompileError { + arrayDim = 0; + int type = i.getType(); + if (type == IntConstant || type == CharConstant) + exprType = (type == IntConstant ? INT : CHAR); + else + exprType = LONG; + } + + public void atDoubleConst(DoubleConst d) throws CompileError { + arrayDim = 0; + if (d.getType() == DoubleConstant) + exprType = DOUBLE; + else + exprType = FLOAT; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTList.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTList.java new file mode 100644 index 0000000..9c6966d --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTList.java @@ -0,0 +1,160 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * A linked list. + * The right subtree must be an ASTList object or null. + */ +public class ASTList extends ASTree { + private ASTree left; + private ASTList right; + + public ASTList(ASTree _head, ASTList _tail) { + left = _head; + right = _tail; + } + + public ASTList(ASTree _head) { + left = _head; + right = null; + } + + public static ASTList make(ASTree e1, ASTree e2, ASTree e3) { + return new ASTList(e1, new ASTList(e2, new ASTList(e3))); + } + + public ASTree getLeft() { return left; } + + public ASTree getRight() { return right; } + + public void setLeft(ASTree _left) { left = _left; } + + public void setRight(ASTree _right) { + right = (ASTList)_right; + } + + /** + * Returns the car part of the list. + */ + public ASTree head() { return left; } + + public void setHead(ASTree _head) { + left = _head; + } + + /** + * Returns the cdr part of the list. + */ + public ASTList tail() { return right; } + + public void setTail(ASTList _tail) { + right = _tail; + } + + public void accept(Visitor v) throws CompileError { v.atASTList(this); } + + public String toString() { + StringBuffer sbuf = new StringBuffer(); + sbuf.append("(<"); + sbuf.append(getTag()); + sbuf.append('>'); + ASTList list = this; + while (list != null) { + sbuf.append(' '); + ASTree a = list.left; + sbuf.append(a == null ? "" : a.toString()); + list = list.right; + } + + sbuf.append(')'); + return sbuf.toString(); + } + + /** + * Returns the number of the elements in this list. + */ + public int length() { + return length(this); + } + + public static int length(ASTList list) { + if (list == null) + return 0; + + int n = 0; + while (list != null) { + list = list.right; + ++n; + } + + return n; + } + + /** + * Returns a sub list of the list. The sub list begins with the + * n-th element of the list. + * + * @param nth zero or more than zero. + */ + public ASTList sublist(int nth) { + ASTList list = this; + while (nth-- > 0) + list = list.right; + + return list; + } + + /** + * Substitutes newObj for oldObj in the + * list. + */ + public boolean subst(ASTree newObj, ASTree oldObj) { + for (ASTList list = this; list != null; list = list.right) + if (list.left == oldObj) { + list.left = newObj; + return true; + } + + return false; + } + + /** + * Appends an object to a list. + */ + public static ASTList append(ASTList a, ASTree b) { + return concat(a, new ASTList(b)); + } + + /** + * Concatenates two lists. + */ + public static ASTList concat(ASTList a, ASTList b) { + if (a == null) + return b; + else { + ASTList list = a; + while (list.right != null) + list = list.right; + + list.right = b; + return a; + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTree.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTree.java new file mode 100644 index 0000000..b464ec0 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTree.java @@ -0,0 +1,59 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import java.io.Serializable; +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Abstract Syntax Tree. An ASTree object represents a node of + * a binary tree. If the node is a leaf node, both getLeft() + * and getRight() returns null. + */ +public abstract class ASTree implements Serializable { + public ASTree getLeft() { return null; } + + public ASTree getRight() { return null; } + + public void setLeft(ASTree _left) {} + + public void setRight(ASTree _right) {} + + /** + * Is a method for the visitor pattern. It calls + * atXXX() on the given visitor, where + * XXX is the class name of the node object. + */ + public abstract void accept(Visitor v) throws CompileError; + + public String toString() { + StringBuffer sbuf = new StringBuffer(); + sbuf.append('<'); + sbuf.append(getTag()); + sbuf.append('>'); + return sbuf.toString(); + } + + /** + * Returns the type of this node. This method is used by + * toString(). + */ + protected String getTag() { + String name = getClass().getName(); + return name.substring(name.lastIndexOf('.') + 1); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java new file mode 100644 index 0000000..aff9430 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java @@ -0,0 +1,32 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Array initializer such as { 1, 2, 3 }. + */ +public class ArrayInit extends ASTList { + public ArrayInit(ASTree firstElement) { + super(firstElement); + } + + public void accept(Visitor v) throws CompileError { v.atArrayInit(this); } + + public String getTag() { return "array"; } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java new file mode 100644 index 0000000..5e49831 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java @@ -0,0 +1,41 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Assignment expression. + */ +public class AssignExpr extends Expr { + /* operator must be either of: + * =, %=, &=, *=, +=, -=, /=, ^=, |=, <<=, >>=, >>>= + */ + + private AssignExpr(int op, ASTree _head, ASTList _tail) { + super(op, _head, _tail); + } + + public static AssignExpr makeAssign(int op, ASTree oprand1, + ASTree oprand2) { + return new AssignExpr(op, oprand1, new ASTList(oprand2)); + } + + public void accept(Visitor v) throws CompileError { + v.atAssignExpr(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java new file mode 100644 index 0000000..41005c1 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java @@ -0,0 +1,42 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Binary expression. + * + *

If the operator is +, the right node might be null. + * See TypeChecker.atBinExpr(). + */ +public class BinExpr extends Expr { + /* operator must be either of: + * ||, &&, |, ^, &, ==, !=, <=, >=, <, >, + * <<, >>, >>>, +, -, *, /, % + */ + + private BinExpr(int op, ASTree _head, ASTList _tail) { + super(op, _head, _tail); + } + + public static BinExpr makeBin(int op, ASTree oprand1, ASTree oprand2) { + return new BinExpr(op, oprand1, new ASTList(oprand2)); + } + + public void accept(Visitor v) throws CompileError { v.atBinExpr(this); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java new file mode 100644 index 0000000..f2b1fb1 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java @@ -0,0 +1,47 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; +import com.wenshuo.agent.javassist.compiler.TokenId; +import com.wenshuo.agent.javassist.compiler.MemberResolver; + +/** + * Method call expression. + */ +public class CallExpr extends Expr { + private MemberResolver.Method method; // cached result of lookupMethod() + + private CallExpr(ASTree _head, ASTList _tail) { + super(TokenId.CALL, _head, _tail); + method = null; + } + + public void setMethod(MemberResolver.Method m) { + method = m; + } + + public MemberResolver.Method getMethod() { + return method; + } + + public static CallExpr makeCall(ASTree target, ASTree args) { + return new CallExpr(target, new ASTList(args)); + } + + public void accept(Visitor v) throws CompileError { v.atCallExpr(this); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java new file mode 100644 index 0000000..60f7b43 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java @@ -0,0 +1,56 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.TokenId; +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Cast expression. + */ +public class CastExpr extends ASTList implements TokenId { + protected int castType; + protected int arrayDim; + + public CastExpr(ASTList className, int dim, ASTree expr) { + super(className, new ASTList(expr)); + castType = CLASS; + arrayDim = dim; + } + + public CastExpr(int type, int dim, ASTree expr) { + super(null, new ASTList(expr)); + castType = type; + arrayDim = dim; + } + + /* Returns CLASS, BOOLEAN, INT, or ... + */ + public int getType() { return castType; } + + public int getArrayDim() { return arrayDim; } + + public ASTList getClassName() { return (ASTList)getLeft(); } + + public ASTree getOprand() { return getRight().getLeft(); } + + public void setOprand(ASTree t) { getRight().setLeft(t); } + + public String getTag() { return "cast:" + castType + ":" + arrayDim; } + + public void accept(Visitor v) throws CompileError { v.atCastExpr(this); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java new file mode 100644 index 0000000..f11690f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java @@ -0,0 +1,44 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Conditional expression. + */ +public class CondExpr extends ASTList { + public CondExpr(ASTree cond, ASTree thenp, ASTree elsep) { + super(cond, new ASTList(thenp, new ASTList(elsep))); + } + + public ASTree condExpr() { return head(); } + + public void setCond(ASTree t) { setHead(t); } + + public ASTree thenExpr() { return tail().head(); } + + public void setThen(ASTree t) { tail().setHead(t); } + + public ASTree elseExpr() { return tail().tail().head(); } + + public void setElse(ASTree t) { tail().tail().setHead(t); } + + public String getTag() { return "?:"; } + + public void accept(Visitor v) throws CompileError { v.atCondExpr(this); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Declarator.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Declarator.java new file mode 100644 index 0000000..be7c1cd --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Declarator.java @@ -0,0 +1,128 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.TokenId; +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Variable declarator. + */ +public class Declarator extends ASTList implements TokenId { + protected int varType; + protected int arrayDim; + protected int localVar; + protected String qualifiedClass; // JVM-internal representation + + public Declarator(int type, int dim) { + super(null); + varType = type; + arrayDim = dim; + localVar = -1; + qualifiedClass = null; + } + + public Declarator(ASTList className, int dim) { + super(null); + varType = CLASS; + arrayDim = dim; + localVar = -1; + qualifiedClass = astToClassName(className, '/'); + } + + /* For declaring a pre-defined? local variable. + */ + public Declarator(int type, String jvmClassName, int dim, + int var, Symbol sym) { + super(null); + varType = type; + arrayDim = dim; + localVar = var; + qualifiedClass = jvmClassName; + setLeft(sym); + append(this, null); // initializer + } + + public Declarator make(Symbol sym, int dim, ASTree init) { + Declarator d = new Declarator(this.varType, this.arrayDim + dim); + d.qualifiedClass = this.qualifiedClass; + d.setLeft(sym); + append(d, init); + return d; + } + + /* Returns CLASS, BOOLEAN, BYTE, CHAR, SHORT, INT, LONG, FLOAT, + * or DOUBLE (or VOID) + */ + public int getType() { return varType; } + + public int getArrayDim() { return arrayDim; } + + public void addArrayDim(int d) { arrayDim += d; } + + public String getClassName() { return qualifiedClass; } + + public void setClassName(String s) { qualifiedClass = s; } + + public Symbol getVariable() { return (Symbol)getLeft(); } + + public void setVariable(Symbol sym) { setLeft(sym); } + + public ASTree getInitializer() { + ASTList t = tail(); + if (t != null) + return t.head(); + else + return null; + } + + public void setLocalVar(int n) { localVar = n; } + + public int getLocalVar() { return localVar; } + + public String getTag() { return "decl"; } + + public void accept(Visitor v) throws CompileError { + v.atDeclarator(this); + } + + public static String astToClassName(ASTList name, char sep) { + if (name == null) + return null; + + StringBuffer sbuf = new StringBuffer(); + astToClassName(sbuf, name, sep); + return sbuf.toString(); + } + + private static void astToClassName(StringBuffer sbuf, ASTList name, + char sep) { + for (;;) { + ASTree h = name.head(); + if (h instanceof Symbol) + sbuf.append(((Symbol)h).get()); + else if (h instanceof ASTList) + astToClassName(sbuf, (ASTList)h, sep); + + name = name.tail(); + if (name == null) + break; + + sbuf.append(sep); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java new file mode 100644 index 0000000..777e3cf --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java @@ -0,0 +1,95 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; +import com.wenshuo.agent.javassist.compiler.TokenId; + +/** + * Double constant. + */ +public class DoubleConst extends ASTree { + protected double value; + protected int type; + + public DoubleConst(double v, int tokenId) { value = v; type = tokenId; } + + public double get() { return value; } + + public void set(double v) { value = v; } + + /* Returns DoubleConstant or FloatConstant + */ + public int getType() { return type; } + + public String toString() { return Double.toString(value); } + + public void accept(Visitor v) throws CompileError { + v.atDoubleConst(this); + } + + public ASTree compute(int op, ASTree right) { + if (right instanceof IntConst) + return compute0(op, (IntConst)right); + else if (right instanceof DoubleConst) + return compute0(op, (DoubleConst)right); + else + return null; + } + + private DoubleConst compute0(int op, DoubleConst right) { + int newType; + if (this.type == TokenId.DoubleConstant + || right.type == TokenId.DoubleConstant) + newType = TokenId.DoubleConstant; + else + newType = TokenId.FloatConstant; + + return compute(op, this.value, right.value, newType); + } + + private DoubleConst compute0(int op, IntConst right) { + return compute(op, this.value, (double)right.value, this.type); + } + + private static DoubleConst compute(int op, double value1, double value2, + int newType) + { + double newValue; + switch (op) { + case '+' : + newValue = value1 + value2; + break; + case '-' : + newValue = value1 - value2; + break; + case '*' : + newValue = value1 * value2; + break; + case '/' : + newValue = value1 / value2; + break; + case '%' : + newValue = value1 % value2; + break; + default : + return null; + } + + return new DoubleConst(newValue, newType); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Expr.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Expr.java new file mode 100644 index 0000000..d49ca3e --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Expr.java @@ -0,0 +1,85 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.TokenId; +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Expression. + */ +public class Expr extends ASTList implements TokenId { + /* operator must be either of: + * (unary) +, (unary) -, ++, --, !, ~, + * ARRAY, . (dot), MEMBER (static member access). + * Otherwise, the object should be an instance of a subclass. + */ + + protected int operatorId; + + Expr(int op, ASTree _head, ASTList _tail) { + super(_head, _tail); + operatorId = op; + } + + Expr(int op, ASTree _head) { + super(_head); + operatorId = op; + } + + public static Expr make(int op, ASTree oprand1, ASTree oprand2) { + return new Expr(op, oprand1, new ASTList(oprand2)); + } + + public static Expr make(int op, ASTree oprand1) { + return new Expr(op, oprand1); + } + + public int getOperator() { return operatorId; } + + public void setOperator(int op) { operatorId = op; } + + public ASTree oprand1() { return getLeft(); } + + public void setOprand1(ASTree expr) { + setLeft(expr); + } + + public ASTree oprand2() { return getRight().getLeft(); } + + public void setOprand2(ASTree expr) { + getRight().setLeft(expr); + } + + public void accept(Visitor v) throws CompileError { v.atExpr(this); } + + public String getName() { + int id = operatorId; + if (id < 128) + return String.valueOf((char)id); + else if (NEQ <= id && id <= ARSHIFT_E) + return opNames[id - NEQ]; + else if (id == INSTANCEOF) + return "instanceof"; + else + return String.valueOf(id); + } + + protected String getTag() { + return "op:" + getName(); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java new file mode 100644 index 0000000..968a97b --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java @@ -0,0 +1,35 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +public class FieldDecl extends ASTList { + public FieldDecl(ASTree _head, ASTList _tail) { + super(_head, _tail); + } + + public ASTList getModifiers() { return (ASTList)getLeft(); } + + public Declarator getDeclarator() { return (Declarator)tail().head(); } + + public ASTree getInit() { return (ASTree)sublist(2).head(); } + + public void accept(Visitor v) throws CompileError { + v.atFieldDecl(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java new file mode 100644 index 0000000..e7fa6d3 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java @@ -0,0 +1,40 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Instanceof expression. + */ +public class InstanceOfExpr extends CastExpr { + public InstanceOfExpr(ASTList className, int dim, ASTree expr) { + super(className, dim, expr); + } + + public InstanceOfExpr(int type, int dim, ASTree expr) { + super(type, dim, expr); + } + + public String getTag() { + return "instanceof:" + castType + ":" + arrayDim; + } + + public void accept(Visitor v) throws CompileError { + v.atInstanceOfExpr(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/IntConst.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/IntConst.java new file mode 100644 index 0000000..b4d83ca --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/IntConst.java @@ -0,0 +1,139 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; +import com.wenshuo.agent.javassist.compiler.TokenId; + +/** + * Integer constant. + */ +public class IntConst extends ASTree { + protected long value; + protected int type; + + public IntConst(long v, int tokenId) { value = v; type = tokenId; } + + public long get() { return value; } + + public void set(long v) { value = v; } + + /* Returns IntConstant, CharConstant, or LongConstant. + */ + public int getType() { return type; } + + public String toString() { return Long.toString(value); } + + public void accept(Visitor v) throws CompileError { + v.atIntConst(this); + } + + public ASTree compute(int op, ASTree right) { + if (right instanceof IntConst) + return compute0(op, (IntConst)right); + else if (right instanceof DoubleConst) + return compute0(op, (DoubleConst)right); + else + return null; + } + + private IntConst compute0(int op, IntConst right) { + int type1 = this.type; + int type2 = right.type; + int newType; + if (type1 == TokenId.LongConstant || type2 == TokenId.LongConstant) + newType = TokenId.LongConstant; + else if (type1 == TokenId.CharConstant + && type2 == TokenId.CharConstant) + newType = TokenId.CharConstant; + else + newType = TokenId.IntConstant; + + long value1 = this.value; + long value2 = right.value; + long newValue; + switch (op) { + case '+' : + newValue = value1 + value2; + break; + case '-' : + newValue = value1 - value2; + break; + case '*' : + newValue = value1 * value2; + break; + case '/' : + newValue = value1 / value2; + break; + case '%' : + newValue = value1 % value2; + break; + case '|' : + newValue = value1 | value2; + break; + case '^' : + newValue = value1 ^ value2; + break; + case '&' : + newValue = value1 & value2; + break; + case TokenId.LSHIFT : + newValue = value << (int)value2; + newType = type1; + break; + case TokenId.RSHIFT : + newValue = value >> (int)value2; + newType = type1; + break; + case TokenId.ARSHIFT : + newValue = value >>> (int)value2; + newType = type1; + break; + default : + return null; + } + + return new IntConst(newValue, newType); + } + + private DoubleConst compute0(int op, DoubleConst right) { + double value1 = (double)this.value; + double value2 = right.value; + double newValue; + switch (op) { + case '+' : + newValue = value1 + value2; + break; + case '-' : + newValue = value1 - value2; + break; + case '*' : + newValue = value1 * value2; + break; + case '/' : + newValue = value1 / value2; + break; + case '%' : + newValue = value1 % value2; + break; + default : + return null; + } + + return new DoubleConst(newValue, right.type); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Keyword.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Keyword.java new file mode 100644 index 0000000..d1b63dd --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Keyword.java @@ -0,0 +1,36 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Keyword. + */ +public class Keyword extends ASTree { + protected int tokenId; + + public Keyword(int token) { + tokenId = token; + } + + public int get() { return tokenId; } + + public String toString() { return "id:" + tokenId; } + + public void accept(Visitor v) throws CompileError { v.atKeyword(this); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Member.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Member.java new file mode 100644 index 0000000..cc5c4d1 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Member.java @@ -0,0 +1,40 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; +import com.wenshuo.agent.javassist.CtField; + +/** + * Member name. + */ +public class Member extends Symbol { + // cache maintained by fieldAccess() in TypeChecker. + // this is used to obtain the value of a static final field. + private CtField field; + + public Member(String name) { + super(name); + field = null; + } + + public void setField(CtField f) { field = f; } + + public CtField getField() { return field; } + + public void accept(Visitor v) throws CompileError { v.atMember(this); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java new file mode 100644 index 0000000..a80e374 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java @@ -0,0 +1,46 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +public class MethodDecl extends ASTList { + public static final String initName = ""; + + public MethodDecl(ASTree _head, ASTList _tail) { + super(_head, _tail); + } + + public boolean isConstructor() { + Symbol sym = getReturn().getVariable(); + return sym != null && initName.equals(sym.get()); + } + + public ASTList getModifiers() { return (ASTList)getLeft(); } + + public Declarator getReturn() { return (Declarator)tail().head(); } + + public ASTList getParams() { return (ASTList)sublist(2).head(); } + + public ASTList getThrows() { return (ASTList)sublist(3).head(); } + + public Stmnt getBody() { return (Stmnt)sublist(4).head(); } + + public void accept(Visitor v) throws CompileError { + v.atMethodDecl(this); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java new file mode 100644 index 0000000..4b064f2 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java @@ -0,0 +1,78 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.TokenId; +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * New Expression. + */ +public class NewExpr extends ASTList implements TokenId { + protected boolean newArray; + protected int arrayType; + + public NewExpr(ASTList className, ASTList args) { + super(className, new ASTList(args)); + newArray = false; + arrayType = CLASS; + } + + public NewExpr(int type, ASTList arraySize, ArrayInit init) { + super(null, new ASTList(arraySize)); + newArray = true; + arrayType = type; + if (init != null) + append(this, init); + } + + public static NewExpr makeObjectArray(ASTList className, + ASTList arraySize, ArrayInit init) { + NewExpr e = new NewExpr(className, arraySize); + e.newArray = true; + if (init != null) + append(e, init); + + return e; + } + + public boolean isArray() { return newArray; } + + /* TokenId.CLASS, TokenId.INT, ... + */ + public int getArrayType() { return arrayType; } + + public ASTList getClassName() { return (ASTList)getLeft(); } + + public ASTList getArguments() { return (ASTList)getRight().getLeft(); } + + public ASTList getArraySize() { return getArguments(); } + + public ArrayInit getInitializer() { + ASTree t = getRight().getRight(); + if (t == null) + return null; + else + return (ArrayInit)t.getLeft(); + } + + public void accept(Visitor v) throws CompileError { v.atNewExpr(this); } + + protected String getTag() { + return newArray ? "new[]" : "new"; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Pair.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Pair.java new file mode 100644 index 0000000..56cb3e6 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Pair.java @@ -0,0 +1,52 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * A node of a a binary tree. This class provides concrete methods + * overriding abstract methods in ASTree. + */ +public class Pair extends ASTree { + protected ASTree left, right; + + public Pair(ASTree _left, ASTree _right) { + left = _left; + right = _right; + } + + public void accept(Visitor v) throws CompileError { v.atPair(this); } + + public String toString() { + StringBuffer sbuf = new StringBuffer(); + sbuf.append("( "); + sbuf.append(left == null ? "" : left.toString()); + sbuf.append(" . "); + sbuf.append(right == null ? "" : right.toString()); + sbuf.append(')'); + return sbuf.toString(); + } + + public ASTree getLeft() { return left; } + + public ASTree getRight() { return right; } + + public void setLeft(ASTree _left) { left = _left; } + + public void setRight(ASTree _right) { right = _right; } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java new file mode 100644 index 0000000..0d81286 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java @@ -0,0 +1,60 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.TokenId; +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Statement. + */ +public class Stmnt extends ASTList implements TokenId { + protected int operatorId; + + public Stmnt(int op, ASTree _head, ASTList _tail) { + super(_head, _tail); + operatorId = op; + } + + public Stmnt(int op, ASTree _head) { + super(_head); + operatorId = op; + } + + public Stmnt(int op) { + this(op, null); + } + + public static Stmnt make(int op, ASTree oprand1, ASTree oprand2) { + return new Stmnt(op, oprand1, new ASTList(oprand2)); + } + + public static Stmnt make(int op, ASTree op1, ASTree op2, ASTree op3) { + return new Stmnt(op, op1, new ASTList(op2, new ASTList(op3))); + } + + public void accept(Visitor v) throws CompileError { v.atStmnt(this); } + + public int getOperator() { return operatorId; } + + protected String getTag() { + if (operatorId < 128) + return "stmnt:" + (char)operatorId; + else + return "stmnt:" + operatorId; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/StringL.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/StringL.java new file mode 100644 index 0000000..d7c43c0 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/StringL.java @@ -0,0 +1,36 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * String literal. + */ +public class StringL extends ASTree { + protected String text; + + public StringL(String t) { + text = t; + } + + public String get() { return text; } + + public String toString() { return "\"" + text + "\""; } + + public void accept(Visitor v) throws CompileError { v.atStringL(this); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Symbol.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Symbol.java new file mode 100644 index 0000000..b305cbd --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Symbol.java @@ -0,0 +1,36 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Identifier. + */ +public class Symbol extends ASTree { + protected String identifier; + + public Symbol(String sym) { + identifier = sym; + } + + public String get() { return identifier; } + + public String toString() { return identifier; } + + public void accept(Visitor v) throws CompileError { v.atSymbol(this); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Variable.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Variable.java new file mode 100644 index 0000000..0b7741a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Variable.java @@ -0,0 +1,39 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * Variable. + */ +public class Variable extends Symbol { + protected Declarator declarator; + + public Variable(String sym, Declarator d) { + super(sym); + declarator = d; + } + + public Declarator getDeclarator() { return declarator; } + + public String toString() { + return identifier + ":" + declarator.getType(); + } + + public void accept(Visitor v) throws CompileError { v.atVariable(this); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Visitor.java b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Visitor.java new file mode 100644 index 0000000..e4c6e44 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Visitor.java @@ -0,0 +1,52 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.compiler.ast; + +import com.wenshuo.agent.javassist.compiler.CompileError; + +/** + * The visitor pattern. + * + * @see ASTree#accept(Visitor) + */ +public class Visitor { + public void atASTList(ASTList n) throws CompileError {} + public void atPair(Pair n) throws CompileError {} + + public void atFieldDecl(FieldDecl n) throws CompileError {} + public void atMethodDecl(MethodDecl n) throws CompileError {} + public void atStmnt(Stmnt n) throws CompileError {} + public void atDeclarator(Declarator n) throws CompileError {} + + public void atAssignExpr(AssignExpr n) throws CompileError {} + public void atCondExpr(CondExpr n) throws CompileError {} + public void atBinExpr(BinExpr n) throws CompileError {} + public void atExpr(Expr n) throws CompileError {} + public void atCallExpr(CallExpr n) throws CompileError {} + public void atCastExpr(CastExpr n) throws CompileError {} + public void atInstanceOfExpr(InstanceOfExpr n) throws CompileError {} + public void atNewExpr(NewExpr n) throws CompileError {} + + public void atSymbol(Symbol n) throws CompileError {} + public void atMember(Member n) throws CompileError {} + public void atVariable(Variable n) throws CompileError {} + public void atKeyword(Keyword n) throws CompileError {} + public void atStringL(StringL n) throws CompileError {} + public void atIntConst(IntConst n) throws CompileError {} + public void atDoubleConst(DoubleConst n) throws CompileError {} + public void atArrayInit(ArrayInit n) throws CompileError {} +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java b/src/main/java/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java new file mode 100644 index 0000000..3942bd7 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java @@ -0,0 +1,270 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.CodeConverter.ArrayAccessReplacementMethodNames; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.Descriptor; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; +import com.wenshuo.agent.javassist.bytecode.analysis.Analyzer; +import com.wenshuo.agent.javassist.bytecode.analysis.Frame; + +/** + * A transformer which replaces array access with static method invocations. + * + * @author Kabir Khan + * @author Jason T. Greene + * @version $Revision: 1.8 $ + */ +public final class TransformAccessArrayField extends Transformer { + private final String methodClassname; + private final ArrayAccessReplacementMethodNames names; + private Frame[] frames; + private int offset; + + public TransformAccessArrayField(Transformer next, String methodClassname, + ArrayAccessReplacementMethodNames names) throws NotFoundException { + super(next); + this.methodClassname = methodClassname; + this.names = names; + + } + + public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException { + /* + * This transformer must be isolated from other transformers, since some + * of them affect the local variable and stack maximums without updating + * the code attribute to reflect the changes. This screws up the + * data-flow analyzer, since it relies on consistent code state. Even + * if the attribute values were updated correctly, we would have to + * detect it, and redo analysis, which is not cheap. Instead, we are + * better off doing all changes in initialize() before everyone else has + * a chance to muck things up. + */ + CodeIterator iterator = minfo.getCodeAttribute().iterator(); + while (iterator.hasNext()) { + try { + int pos = iterator.next(); + int c = iterator.byteAt(pos); + + if (c == AALOAD) + initFrames(clazz, minfo); + + if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD + || c == FALOAD || c == IALOAD || c == LALOAD + || c == SALOAD) { + pos = replace(cp, iterator, pos, c, getLoadReplacementSignature(c)); + } else if (c == AASTORE || c == BASTORE || c == CASTORE + || c == DASTORE || c == FASTORE || c == IASTORE + || c == LASTORE || c == SASTORE) { + pos = replace(cp, iterator, pos, c, getStoreReplacementSignature(c)); + } + + } catch (Exception e) { + throw new CannotCompileException(e); + } + } + } + + public void clean() { + frames = null; + offset = -1; + } + + public int transform(CtClass tclazz, int pos, CodeIterator iterator, + ConstPool cp) throws BadBytecode { + // Do nothing, see above comment + return pos; + } + + private Frame getFrame(int pos) throws BadBytecode { + return frames[pos - offset]; // Adjust pos + } + + private void initFrames(CtClass clazz, MethodInfo minfo) throws BadBytecode { + if (frames == null) { + frames = ((new Analyzer())).analyze(clazz, minfo); + offset = 0; // start tracking changes + } + } + + private int updatePos(int pos, int increment) { + if (offset > -1) + offset += increment; + + return pos + increment; + } + + private String getTopType(int pos) throws BadBytecode { + Frame frame = getFrame(pos); + if (frame == null) + return null; + + CtClass clazz = frame.peek().getCtClass(); + return clazz != null ? Descriptor.toJvmName(clazz) : null; + } + + private int replace(ConstPool cp, CodeIterator iterator, int pos, + int opcode, String signature) throws BadBytecode { + String castType = null; + String methodName = getMethodName(opcode); + if (methodName != null) { + // See if the object must be cast + if (opcode == AALOAD) { + castType = getTopType(iterator.lookAhead()); + // Do not replace an AALOAD instruction that we do not have a type for + // This happens when the state is guaranteed to be null (Type.UNINIT) + // So we don't really care about this case. + if (castType == null) + return pos; + if ("java/lang/Object".equals(castType)) + castType = null; + } + + // The gap may include extra padding + // Write a nop in case the padding pushes the instruction forward + iterator.writeByte(NOP, pos); + CodeIterator.Gap gap + = iterator.insertGapAt(pos, castType != null ? 5 : 2, false); + pos = gap.position; + int mi = cp.addClassInfo(methodClassname); + int methodref = cp.addMethodrefInfo(mi, methodName, signature); + iterator.writeByte(INVOKESTATIC, pos); + iterator.write16bit(methodref, pos + 1); + + if (castType != null) { + int index = cp.addClassInfo(castType); + iterator.writeByte(CHECKCAST, pos + 3); + iterator.write16bit(index, pos + 4); + } + + pos = updatePos(pos, gap.length); + } + + return pos; + } + + private String getMethodName(int opcode) { + String methodName = null; + switch (opcode) { + case AALOAD: + methodName = names.objectRead(); + break; + case BALOAD: + methodName = names.byteOrBooleanRead(); + break; + case CALOAD: + methodName = names.charRead(); + break; + case DALOAD: + methodName = names.doubleRead(); + break; + case FALOAD: + methodName = names.floatRead(); + break; + case IALOAD: + methodName = names.intRead(); + break; + case SALOAD: + methodName = names.shortRead(); + break; + case LALOAD: + methodName = names.longRead(); + break; + case AASTORE: + methodName = names.objectWrite(); + break; + case BASTORE: + methodName = names.byteOrBooleanWrite(); + break; + case CASTORE: + methodName = names.charWrite(); + break; + case DASTORE: + methodName = names.doubleWrite(); + break; + case FASTORE: + methodName = names.floatWrite(); + break; + case IASTORE: + methodName = names.intWrite(); + break; + case SASTORE: + methodName = names.shortWrite(); + break; + case LASTORE: + methodName = names.longWrite(); + break; + } + + if (methodName.equals("")) + methodName = null; + + return methodName; + } + + private String getLoadReplacementSignature(int opcode) throws BadBytecode { + switch (opcode) { + case AALOAD: + return "(Ljava/lang/Object;I)Ljava/lang/Object;"; + case BALOAD: + return "(Ljava/lang/Object;I)B"; + case CALOAD: + return "(Ljava/lang/Object;I)C"; + case DALOAD: + return "(Ljava/lang/Object;I)D"; + case FALOAD: + return "(Ljava/lang/Object;I)F"; + case IALOAD: + return "(Ljava/lang/Object;I)I"; + case SALOAD: + return "(Ljava/lang/Object;I)S"; + case LALOAD: + return "(Ljava/lang/Object;I)J"; + } + + throw new BadBytecode(opcode); + } + + private String getStoreReplacementSignature(int opcode) throws BadBytecode { + switch (opcode) { + case AASTORE: + return "(Ljava/lang/Object;ILjava/lang/Object;)V"; + case BASTORE: + return "(Ljava/lang/Object;IB)V"; + case CASTORE: + return "(Ljava/lang/Object;IC)V"; + case DASTORE: + return "(Ljava/lang/Object;ID)V"; + case FASTORE: + return "(Ljava/lang/Object;IF)V"; + case IASTORE: + return "(Ljava/lang/Object;II)V"; + case SASTORE: + return "(Ljava/lang/Object;IS)V"; + case LASTORE: + return "(Ljava/lang/Object;IJ)V"; + } + + throw new BadBytecode(opcode); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformAfter.java b/src/main/java/com/wenshuo/agent/javassist/convert/TransformAfter.java new file mode 100644 index 0000000..ebe8c61 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/TransformAfter.java @@ -0,0 +1,47 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.*; + +public class TransformAfter extends TransformBefore { + public TransformAfter(Transformer next, + CtMethod origMethod, CtMethod afterMethod) + throws NotFoundException + { + super(next, origMethod, afterMethod); + } + + protected int match2(int pos, CodeIterator iterator) throws BadBytecode { + iterator.move(pos); + iterator.insert(saveCode); + iterator.insert(loadCode); + int p = iterator.insertGap(3); + iterator.setMark(p); + iterator.insert(loadCode); + pos = iterator.next(); + p = iterator.getMark(); + iterator.writeByte(iterator.byteAt(pos), p); + iterator.write16bit(iterator.u16bitAt(pos + 1), p + 1); + iterator.writeByte(INVOKESTATIC, pos); + iterator.write16bit(newIndex, pos + 1); + iterator.move(p); + return iterator.next(); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformBefore.java b/src/main/java/com/wenshuo/agent/javassist/convert/TransformBefore.java new file mode 100644 index 0000000..ff75939 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/TransformBefore.java @@ -0,0 +1,108 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.*; + +public class TransformBefore extends TransformCall { + protected CtClass[] parameterTypes; + protected int locals; + protected int maxLocals; + protected byte[] saveCode, loadCode; + + public TransformBefore(Transformer next, + CtMethod origMethod, CtMethod beforeMethod) + throws NotFoundException + { + super(next, origMethod, beforeMethod); + + // override + methodDescriptor = origMethod.getMethodInfo2().getDescriptor(); + + parameterTypes = origMethod.getParameterTypes(); + locals = 0; + maxLocals = 0; + saveCode = loadCode = null; + } + + public void initialize(ConstPool cp, CodeAttribute attr) { + super.initialize(cp, attr); + locals = 0; + maxLocals = attr.getMaxLocals(); + saveCode = loadCode = null; + } + + protected int match(int c, int pos, CodeIterator iterator, + int typedesc, ConstPool cp) throws BadBytecode + { + if (newIndex == 0) { + String desc = Descriptor.ofParameters(parameterTypes) + 'V'; + desc = Descriptor.insertParameter(classname, desc); + int nt = cp.addNameAndTypeInfo(newMethodname, desc); + int ci = cp.addClassInfo(newClassname); + newIndex = cp.addMethodrefInfo(ci, nt); + constPool = cp; + } + + if (saveCode == null) + makeCode(parameterTypes, cp); + + return match2(pos, iterator); + } + + protected int match2(int pos, CodeIterator iterator) throws BadBytecode { + iterator.move(pos); + iterator.insert(saveCode); + iterator.insert(loadCode); + int p = iterator.insertGap(3); + iterator.writeByte(INVOKESTATIC, p); + iterator.write16bit(newIndex, p + 1); + iterator.insert(loadCode); + return iterator.next(); + } + + public int extraLocals() { return locals; } + + protected void makeCode(CtClass[] paramTypes, ConstPool cp) { + Bytecode save = new Bytecode(cp, 0, 0); + Bytecode load = new Bytecode(cp, 0, 0); + + int var = maxLocals; + int len = (paramTypes == null) ? 0 : paramTypes.length; + load.addAload(var); + makeCode2(save, load, 0, len, paramTypes, var + 1); + save.addAstore(var); + + saveCode = save.get(); + loadCode = load.get(); + } + + private void makeCode2(Bytecode save, Bytecode load, + int i, int n, CtClass[] paramTypes, int var) + { + if (i < n) { + int size = load.addLoad(var, paramTypes[i]); + makeCode2(save, load, i + 1, n, paramTypes, var + size); + save.addStore(var, paramTypes[i]); + } + else + locals = var - maxLocals; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformCall.java b/src/main/java/com/wenshuo/agent/javassist/convert/TransformCall.java new file mode 100644 index 0000000..317bda3 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/TransformCall.java @@ -0,0 +1,130 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.Modifier; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.*; + +public class TransformCall extends Transformer { + protected String classname, methodname, methodDescriptor; + protected String newClassname, newMethodname; + protected boolean newMethodIsPrivate; + + /* cache */ + protected int newIndex; + protected ConstPool constPool; + + public TransformCall(Transformer next, CtMethod origMethod, + CtMethod substMethod) + { + this(next, origMethod.getName(), substMethod); + classname = origMethod.getDeclaringClass().getName(); + } + + public TransformCall(Transformer next, String oldMethodName, + CtMethod substMethod) + { + super(next); + methodname = oldMethodName; + methodDescriptor = substMethod.getMethodInfo2().getDescriptor(); + classname = newClassname = substMethod.getDeclaringClass().getName(); + newMethodname = substMethod.getName(); + constPool = null; + newMethodIsPrivate = Modifier.isPrivate(substMethod.getModifiers()); + } + + public void initialize(ConstPool cp, CodeAttribute attr) { + if (constPool != cp) + newIndex = 0; + } + + /** + * Modify INVOKEINTERFACE, INVOKESPECIAL, INVOKESTATIC and INVOKEVIRTUAL + * so that a different method is invoked. The class name in the operand + * of these instructions might be a subclass of the target class specified + * by classname. This method transforms the instruction + * in that case unless the subclass overrides the target method. + */ + public int transform(CtClass clazz, int pos, CodeIterator iterator, + ConstPool cp) throws BadBytecode + { + int c = iterator.byteAt(pos); + if (c == INVOKEINTERFACE || c == INVOKESPECIAL + || c == INVOKESTATIC || c == INVOKEVIRTUAL) { + int index = iterator.u16bitAt(pos + 1); + String cname = cp.eqMember(methodname, methodDescriptor, index); + if (cname != null && matchClass(cname, clazz.getClassPool())) { + int ntinfo = cp.getMemberNameAndType(index); + pos = match(c, pos, iterator, + cp.getNameAndTypeDescriptor(ntinfo), cp); + } + } + + return pos; + } + + private boolean matchClass(String name, ClassPool pool) { + if (classname.equals(name)) + return true; + + try { + CtClass clazz = pool.get(name); + CtClass declClazz = pool.get(classname); + if (clazz.subtypeOf(declClazz)) + try { + CtMethod m = clazz.getMethod(methodname, methodDescriptor); + return m.getDeclaringClass().getName().equals(classname); + } + catch (NotFoundException e) { + // maybe the original method has been removed. + return true; + } + } + catch (NotFoundException e) { + return false; + } + + return false; + } + + protected int match(int c, int pos, CodeIterator iterator, + int typedesc, ConstPool cp) throws BadBytecode + { + if (newIndex == 0) { + int nt = cp.addNameAndTypeInfo(cp.addUtf8Info(newMethodname), + typedesc); + int ci = cp.addClassInfo(newClassname); + if (c == INVOKEINTERFACE) + newIndex = cp.addInterfaceMethodrefInfo(ci, nt); + else { + if (newMethodIsPrivate && c == INVOKEVIRTUAL) + iterator.writeByte(INVOKESPECIAL, pos); + + newIndex = cp.addMethodrefInfo(ci, nt); + } + + constPool = cp; + } + + iterator.write16bit(newIndex, pos + 1); + return pos; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java b/src/main/java/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java new file mode 100644 index 0000000..08ed468 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java @@ -0,0 +1,82 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtField; +import com.wenshuo.agent.javassist.Modifier; + +final public class TransformFieldAccess extends Transformer { + private String newClassname, newFieldname; + private String fieldname; + private CtClass fieldClass; + private boolean isPrivate; + + /* cache */ + private int newIndex; + private ConstPool constPool; + + public TransformFieldAccess(Transformer next, CtField field, + String newClassname, String newFieldname) + { + super(next); + this.fieldClass = field.getDeclaringClass(); + this.fieldname = field.getName(); + this.isPrivate = Modifier.isPrivate(field.getModifiers()); + this.newClassname = newClassname; + this.newFieldname = newFieldname; + this.constPool = null; + } + + public void initialize(ConstPool cp, CodeAttribute attr) { + if (constPool != cp) + newIndex = 0; + } + + /** + * Modify GETFIELD, GETSTATIC, PUTFIELD, and PUTSTATIC so that + * a different field is accessed. The new field must be declared + * in a superclass of the class in which the original field is + * declared. + */ + public int transform(CtClass clazz, int pos, + CodeIterator iterator, ConstPool cp) + { + int c = iterator.byteAt(pos); + if (c == GETFIELD || c == GETSTATIC + || c == PUTFIELD || c == PUTSTATIC) { + int index = iterator.u16bitAt(pos + 1); + String typedesc + = TransformReadField.isField(clazz.getClassPool(), cp, + fieldClass, fieldname, isPrivate, index); + if (typedesc != null) { + if (newIndex == 0) { + int nt = cp.addNameAndTypeInfo(newFieldname, + typedesc); + newIndex = cp.addFieldrefInfo( + cp.addClassInfo(newClassname), nt); + constPool = cp; + } + + iterator.write16bit(newIndex, pos + 1); + } + } + + return pos; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformNew.java b/src/main/java/com/wenshuo/agent/javassist/convert/TransformNew.java new file mode 100644 index 0000000..a85d750 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/TransformNew.java @@ -0,0 +1,103 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CannotCompileException; + +final public class TransformNew extends Transformer { + private int nested; + private String classname, trapClass, trapMethod; + + public TransformNew(Transformer next, + String classname, String trapClass, String trapMethod) { + super(next); + this.classname = classname; + this.trapClass = trapClass; + this.trapMethod = trapMethod; + } + + public void initialize(ConstPool cp, CodeAttribute attr) { + nested = 0; + } + + /** + * Replace a sequence of + * NEW classname + * DUP + * ... + * INVOKESPECIAL + * with + * NOP + * NOP + * ... + * INVOKESTATIC trapMethod in trapClass + */ + public int transform(CtClass clazz, int pos, CodeIterator iterator, + ConstPool cp) throws CannotCompileException + { + int index; + int c = iterator.byteAt(pos); + if (c == NEW) { + index = iterator.u16bitAt(pos + 1); + if (cp.getClassInfo(index).equals(classname)) { + if (iterator.byteAt(pos + 3) != DUP) + throw new CannotCompileException( + "NEW followed by no DUP was found"); + + iterator.writeByte(NOP, pos); + iterator.writeByte(NOP, pos + 1); + iterator.writeByte(NOP, pos + 2); + iterator.writeByte(NOP, pos + 3); + ++nested; + + StackMapTable smt + = (StackMapTable)iterator.get().getAttribute(StackMapTable.tag); + if (smt != null) + smt.removeNew(pos); + + StackMap sm + = (StackMap)iterator.get().getAttribute(StackMap.tag); + if (sm != null) + sm.removeNew(pos); + } + } + else if (c == INVOKESPECIAL) { + index = iterator.u16bitAt(pos + 1); + int typedesc = cp.isConstructor(classname, index); + if (typedesc != 0 && nested > 0) { + int methodref = computeMethodref(typedesc, cp); + iterator.writeByte(INVOKESTATIC, pos); + iterator.write16bit(methodref, pos + 1); + --nested; + } + } + + return pos; + } + + private int computeMethodref(int typedesc, ConstPool cp) { + int classIndex = cp.addClassInfo(trapClass); + int mnameIndex = cp.addUtf8Info(trapMethod); + typedesc = cp.addUtf8Info( + Descriptor.changeReturnType(classname, + cp.getUtf8Info(typedesc))); + return cp.addMethodrefInfo(classIndex, + cp.addNameAndTypeInfo(mnameIndex, typedesc)); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformNewClass.java b/src/main/java/com/wenshuo/agent/javassist/convert/TransformNewClass.java new file mode 100644 index 0000000..513b8ef --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/TransformNewClass.java @@ -0,0 +1,83 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CannotCompileException; + +final public class TransformNewClass extends Transformer { + private int nested; + private String classname, newClassName; + private int newClassIndex, newMethodNTIndex, newMethodIndex; + + public TransformNewClass(Transformer next, + String classname, String newClassName) { + super(next); + this.classname = classname; + this.newClassName = newClassName; + } + + public void initialize(ConstPool cp, CodeAttribute attr) { + nested = 0; + newClassIndex = newMethodNTIndex = newMethodIndex = 0; + } + + /** + * Modifies a sequence of + * NEW classname + * DUP + * ... + * INVOKESPECIAL classname:method + */ + public int transform(CtClass clazz, int pos, CodeIterator iterator, + ConstPool cp) throws CannotCompileException + { + int index; + int c = iterator.byteAt(pos); + if (c == NEW) { + index = iterator.u16bitAt(pos + 1); + if (cp.getClassInfo(index).equals(classname)) { + if (iterator.byteAt(pos + 3) != DUP) + throw new CannotCompileException( + "NEW followed by no DUP was found"); + + if (newClassIndex == 0) + newClassIndex = cp.addClassInfo(newClassName); + + iterator.write16bit(newClassIndex, pos + 1); + ++nested; + } + } + else if (c == INVOKESPECIAL) { + index = iterator.u16bitAt(pos + 1); + int typedesc = cp.isConstructor(classname, index); + if (typedesc != 0 && nested > 0) { + int nt = cp.getMethodrefNameAndType(index); + if (newMethodNTIndex != nt) { + newMethodNTIndex = nt; + newMethodIndex = cp.addMethodrefInfo(newClassIndex, nt); + } + + iterator.write16bit(newMethodIndex, pos + 1); + --nested; + } + } + + return pos; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformReadField.java b/src/main/java/com/wenshuo/agent/javassist/convert/TransformReadField.java new file mode 100644 index 0000000..d8477c8 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/TransformReadField.java @@ -0,0 +1,96 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtField; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.Modifier; + +public class TransformReadField extends Transformer { + protected String fieldname; + protected CtClass fieldClass; + protected boolean isPrivate; + protected String methodClassname, methodName; + + public TransformReadField(Transformer next, CtField field, + String methodClassname, String methodName) + { + super(next); + this.fieldClass = field.getDeclaringClass(); + this.fieldname = field.getName(); + this.methodClassname = methodClassname; + this.methodName = methodName; + this.isPrivate = Modifier.isPrivate(field.getModifiers()); + } + + static String isField(ClassPool pool, ConstPool cp, CtClass fclass, + String fname, boolean is_private, int index) { + if (!cp.getFieldrefName(index).equals(fname)) + return null; + + try { + CtClass c = pool.get(cp.getFieldrefClassName(index)); + if (c == fclass || (!is_private && isFieldInSuper(c, fclass, fname))) + return cp.getFieldrefType(index); + } + catch (NotFoundException e) {} + return null; + } + + static boolean isFieldInSuper(CtClass clazz, CtClass fclass, String fname) { + if (!clazz.subclassOf(fclass)) + return false; + + try { + CtField f = clazz.getField(fname); + return f.getDeclaringClass() == fclass; + } + catch (NotFoundException e) {} + return false; + } + + public int transform(CtClass tclazz, int pos, CodeIterator iterator, + ConstPool cp) throws BadBytecode + { + int c = iterator.byteAt(pos); + if (c == GETFIELD || c == GETSTATIC) { + int index = iterator.u16bitAt(pos + 1); + String typedesc = isField(tclazz.getClassPool(), cp, + fieldClass, fieldname, isPrivate, index); + if (typedesc != null) { + if (c == GETSTATIC) { + iterator.move(pos); + pos = iterator.insertGap(1); // insertGap() may insert 4 bytes. + iterator.writeByte(ACONST_NULL, pos); + pos = iterator.next(); + } + + String type = "(Ljava/lang/Object;)" + typedesc; + int mi = cp.addClassInfo(methodClassname); + int methodref = cp.addMethodrefInfo(mi, methodName, type); + iterator.writeByte(INVOKESTATIC, pos); + iterator.write16bit(methodref, pos + 1); + return pos; + } + } + + return pos; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformWriteField.java b/src/main/java/com/wenshuo/agent/javassist/convert/TransformWriteField.java new file mode 100644 index 0000000..ecc3f38 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/TransformWriteField.java @@ -0,0 +1,72 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtField; +import com.wenshuo.agent.javassist.bytecode.*; + +final public class TransformWriteField extends TransformReadField { + public TransformWriteField(Transformer next, CtField field, + String methodClassname, String methodName) + { + super(next, field, methodClassname, methodName); + } + + public int transform(CtClass tclazz, int pos, CodeIterator iterator, + ConstPool cp) throws BadBytecode + { + int c = iterator.byteAt(pos); + if (c == PUTFIELD || c == PUTSTATIC) { + int index = iterator.u16bitAt(pos + 1); + String typedesc = isField(tclazz.getClassPool(), cp, + fieldClass, fieldname, isPrivate, index); + if (typedesc != null) { + if (c == PUTSTATIC) { + CodeAttribute ca = iterator.get(); + iterator.move(pos); + char c0 = typedesc.charAt(0); + if (c0 == 'J' || c0 == 'D') { // long or double + // insertGap() may insert 4 bytes. + pos = iterator.insertGap(3); + iterator.writeByte(ACONST_NULL, pos); + iterator.writeByte(DUP_X2, pos + 1); + iterator.writeByte(POP, pos + 2); + ca.setMaxStack(ca.getMaxStack() + 2); + } + else { + // insertGap() may insert 4 bytes. + pos = iterator.insertGap(2); + iterator.writeByte(ACONST_NULL, pos); + iterator.writeByte(SWAP, pos + 1); + ca.setMaxStack(ca.getMaxStack() + 1); + } + + pos = iterator.next(); + } + + int mi = cp.addClassInfo(methodClassname); + String type = "(Ljava/lang/Object;" + typedesc + ")V"; + int methodref = cp.addMethodrefInfo(mi, methodName, type); + iterator.writeByte(INVOKESTATIC, pos); + iterator.write16bit(methodref, pos + 1); + } + } + + return pos; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/Transformer.java b/src/main/java/com/wenshuo/agent/javassist/convert/Transformer.java new file mode 100644 index 0000000..573dcf6 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/convert/Transformer.java @@ -0,0 +1,57 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.convert; + +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.CodeAttribute; +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; +import com.wenshuo.agent.javassist.bytecode.Opcode; + +/** + * Transformer and its subclasses are used for executing + * code transformation specified by CodeConverter. + * + * @see javassist.CodeConverter + */ +public abstract class Transformer implements Opcode { + private Transformer next; + + public Transformer(Transformer t) { + next = t; + } + + public Transformer getNext() { return next; } + + public void initialize(ConstPool cp, CodeAttribute attr) {} + + public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException { + initialize(cp, minfo.getCodeAttribute()); + } + + public void clean() {} + + public abstract int transform(CtClass clazz, int pos, CodeIterator it, + ConstPool cp) throws CannotCompileException, BadBytecode; + + public int extraLocals() { return 0; } + + public int extraStack() { return 0; } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/Cast.java b/src/main/java/com/wenshuo/agent/javassist/expr/Cast.java new file mode 100644 index 0000000..2f47ebf --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/Cast.java @@ -0,0 +1,166 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.*; +import com.wenshuo.agent.javassist.compiler.ast.ASTList; + +/** + * Explicit type cast. + */ +public class Cast extends Expr { + /** + * Undocumented constructor. Do not use; internal-use only. + */ + protected Cast(int pos, CodeIterator i, CtClass declaring, MethodInfo m) { + super(pos, i, declaring, m); + } + + /** + * Returns the method or constructor containing the type cast + * expression represented by this object. + */ + public CtBehavior where() { return super.where(); } + + /** + * Returns the line number of the source line containing the + * type-cast expression. + * + * @return -1 if this information is not available. + */ + public int getLineNumber() { + return super.getLineNumber(); + } + + /** + * Returns the source file containing the type-cast expression. + * + * @return null if this information is not available. + */ + public String getFileName() { + return super.getFileName(); + } + + /** + * Returns the CtClass object representing + * the type specified by the cast. + */ + public CtClass getType() throws NotFoundException { + ConstPool cp = getConstPool(); + int pos = currentPos; + int index = iterator.u16bitAt(pos + 1); + String name = cp.getClassInfo(index); + return thisClass.getClassPool().getCtClass(name); + } + + /** + * Returns the list of exceptions that the expression may throw. + * This list includes both the exceptions that the try-catch statements + * including the expression can catch and the exceptions that + * the throws declaration allows the method to throw. + */ + public CtClass[] mayThrow() { + return super.mayThrow(); + } + + /** + * Replaces the explicit cast operator with the bytecode derived from + * the given source text. + * + *

$0 is available but the value is null. + * + * @param statement a Java statement except try-catch. + */ + public void replace(String statement) throws CannotCompileException { + thisClass.getClassFile(); // to call checkModify(). + ConstPool constPool = getConstPool(); + int pos = currentPos; + int index = iterator.u16bitAt(pos + 1); + + Javac jc = new Javac(thisClass); + ClassPool cp = thisClass.getClassPool(); + CodeAttribute ca = iterator.get(); + + try { + CtClass[] params + = new CtClass[] { cp.get(javaLangObject) }; + CtClass retType = getType(); + + int paramVar = ca.getMaxLocals(); + jc.recordParams(javaLangObject, params, true, paramVar, + withinStatic()); + int retVar = jc.recordReturnType(retType, true); + jc.recordProceed(new ProceedForCast(index, retType)); + + /* Is $_ included in the source code? + */ + checkResultValue(retType, statement); + + Bytecode bytecode = jc.getBytecode(); + storeStack(params, true, paramVar, bytecode); + jc.recordLocalVariables(ca, pos); + + bytecode.addConstZero(retType); + bytecode.addStore(retVar, retType); // initialize $_ + + jc.compileStmnt(statement); + bytecode.addLoad(retVar, retType); + + replace0(pos, bytecode, 3); + } + catch (CompileError e) { throw new CannotCompileException(e); } + catch (NotFoundException e) { throw new CannotCompileException(e); } + catch (BadBytecode e) { + throw new CannotCompileException("broken method"); + } + } + + /* $proceed(Object obj) + */ + static class ProceedForCast implements ProceedHandler { + int index; + CtClass retType; + + ProceedForCast(int i, CtClass t) { + index = i; + retType = t; + } + + public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) + throws CompileError + { + if (gen.getMethodArgsLength(args) != 1) + throw new CompileError(Javac.proceedName + + "() cannot take more than one parameter " + + "for cast"); + + gen.atMethodArgs(args, new int[1], new int[1], new String[1]); + bytecode.addOpcode(Opcode.CHECKCAST); + bytecode.addIndex(index); + gen.setType(retType); + } + + public void setReturnType(JvstTypeChecker c, ASTList args) + throws CompileError + { + c.atMethodArgs(args, new int[1], new int[1], new String[1]); + c.setType(retType); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/ConstructorCall.java b/src/main/java/com/wenshuo/agent/javassist/expr/ConstructorCall.java new file mode 100644 index 0000000..b99bddc --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/ConstructorCall.java @@ -0,0 +1,70 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtConstructor; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; + +/** + * Constructor call such as this() and super() + * within a constructor body. + * + * @see NewExpr + */ +public class ConstructorCall extends MethodCall { + /** + * Undocumented constructor. Do not use; internal-use only. + */ + protected ConstructorCall(int pos, CodeIterator i, CtClass decl, MethodInfo m) { + super(pos, i, decl, m); + } + + /** + * Returns "super" or ""this". + */ + public String getMethodName() { + return isSuper() ? "super" : "this"; + } + + /** + * Always throws a NotFoundException. + * + * @see #getConstructor() + */ + public CtMethod getMethod() throws NotFoundException { + throw new NotFoundException("this is a constructor call. Call getConstructor()."); + } + + /** + * Returns the called constructor. + */ + public CtConstructor getConstructor() throws NotFoundException { + return getCtClass().getConstructor(getSignature()); + } + + /** + * Returns true if the called constructor is not this() + * but super() (a constructor declared in the super class). + */ + public boolean isSuper() { + return super.isSuper(); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/Expr.java b/src/main/java/com/wenshuo/agent/javassist/expr/Expr.java new file mode 100644 index 0000000..6dd56b9 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/Expr.java @@ -0,0 +1,330 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtBehavior; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtConstructor; +import com.wenshuo.agent.javassist.CtPrimitiveType; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.AccessFlag; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.Bytecode; +import com.wenshuo.agent.javassist.bytecode.ClassFile; +import com.wenshuo.agent.javassist.bytecode.CodeAttribute; +import com.wenshuo.agent.javassist.bytecode.CodeIterator; +import com.wenshuo.agent.javassist.bytecode.ConstPool; +import com.wenshuo.agent.javassist.bytecode.ExceptionTable; +import com.wenshuo.agent.javassist.bytecode.ExceptionsAttribute; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; +import com.wenshuo.agent.javassist.bytecode.Opcode; +import com.wenshuo.agent.javassist.compiler.Javac; + +import java.util.Iterator; +import java.util.LinkedList; + +/** + * Expression. + */ +public abstract class Expr implements Opcode { + int currentPos; + CodeIterator iterator; + CtClass thisClass; + MethodInfo thisMethod; + boolean edited; + int maxLocals, maxStack; + + static final String javaLangObject = "java.lang.Object"; + + /** + * Undocumented constructor. Do not use; internal-use only. + */ + protected Expr(int pos, CodeIterator i, CtClass declaring, MethodInfo m) { + currentPos = pos; + iterator = i; + thisClass = declaring; + thisMethod = m; + } + + /** + * Returns the class that declares the method enclosing + * this expression. + * + * @since 3.7 + */ + public CtClass getEnclosingClass() { return thisClass; } + + protected final ConstPool getConstPool() { + return thisMethod.getConstPool(); + } + + protected final boolean edited() { + return edited; + } + + protected final int locals() { + return maxLocals; + } + + protected final int stack() { + return maxStack; + } + + /** + * Returns true if this method is static. + */ + protected final boolean withinStatic() { + return (thisMethod.getAccessFlags() & AccessFlag.STATIC) != 0; + } + + /** + * Returns the constructor or method containing the expression. + */ + public CtBehavior where() { + MethodInfo mi = thisMethod; + CtBehavior[] cb = thisClass.getDeclaredBehaviors(); + for (int i = cb.length - 1; i >= 0; --i) + if (cb[i].getMethodInfo2() == mi) + return cb[i]; + + CtConstructor init = thisClass.getClassInitializer(); + if (init != null && init.getMethodInfo2() == mi) + return init; + + /* getDeclaredBehaviors() returns a list of methods/constructors. + * Although the list is cached in a CtClass object, it might be + * recreated for some reason. Thus, the member name and the signature + * must be also checked. + */ + for (int i = cb.length - 1; i >= 0; --i) { + if (thisMethod.getName().equals(cb[i].getMethodInfo2().getName()) + && thisMethod.getDescriptor() + .equals(cb[i].getMethodInfo2().getDescriptor())) { + return cb[i]; + } + } + + throw new RuntimeException("fatal: not found"); + } + + /** + * Returns the list of exceptions that the expression may throw. This list + * includes both the exceptions that the try-catch statements including the + * expression can catch and the exceptions that the throws declaration + * allows the method to throw. + */ + public CtClass[] mayThrow() { + ClassPool pool = thisClass.getClassPool(); + ConstPool cp = thisMethod.getConstPool(); + LinkedList list = new LinkedList(); + try { + CodeAttribute ca = thisMethod.getCodeAttribute(); + ExceptionTable et = ca.getExceptionTable(); + int pos = currentPos; + int n = et.size(); + for (int i = 0; i < n; ++i) + if (et.startPc(i) <= pos && pos < et.endPc(i)) { + int t = et.catchType(i); + if (t > 0) + try { + addClass(list, pool.get(cp.getClassInfo(t))); + } + catch (NotFoundException e) { + } + } + } + catch (NullPointerException e) { + } + + ExceptionsAttribute ea = thisMethod.getExceptionsAttribute(); + if (ea != null) { + String[] exceptions = ea.getExceptions(); + if (exceptions != null) { + int n = exceptions.length; + for (int i = 0; i < n; ++i) + try { + addClass(list, pool.get(exceptions[i])); + } + catch (NotFoundException e) { + } + } + } + + return (CtClass[])list.toArray(new CtClass[list.size()]); + } + + private static void addClass(LinkedList list, CtClass c) { + Iterator it = list.iterator(); + while (it.hasNext()) + if (it.next() == c) + return; + + list.add(c); + } + + /** + * Returns the index of the bytecode corresponding to the expression. It is + * the index into the byte array containing the Java bytecode that + * implements the method. + */ + public int indexOfBytecode() { + return currentPos; + } + + /** + * Returns the line number of the source line containing the expression. + * + * @return -1 if this information is not available. + */ + public int getLineNumber() { + return thisMethod.getLineNumber(currentPos); + } + + /** + * Returns the source file containing the expression. + * + * @return null if this information is not available. + */ + public String getFileName() { + ClassFile cf = thisClass.getClassFile2(); + if (cf == null) + return null; + else + return cf.getSourceFile(); + } + + static final boolean checkResultValue(CtClass retType, String prog) + throws CannotCompileException { + /* + * Is $_ included in the source code? + */ + boolean hasIt = (prog.indexOf(Javac.resultVarName) >= 0); + if (!hasIt && retType != CtClass.voidType) + throw new CannotCompileException( + "the resulting value is not stored in " + + Javac.resultVarName); + + return hasIt; + } + + /* + * If isStaticCall is true, null is assigned to $0. So $0 must be declared + * by calling Javac.recordParams(). + * + * After executing this method, the current stack depth might be less than + * 0. + */ + static final void storeStack(CtClass[] params, boolean isStaticCall, + int regno, Bytecode bytecode) { + storeStack0(0, params.length, params, regno + 1, bytecode); + if (isStaticCall) + bytecode.addOpcode(ACONST_NULL); + + bytecode.addAstore(regno); + } + + private static void storeStack0(int i, int n, CtClass[] params, int regno, + Bytecode bytecode) { + if (i >= n) + return; + else { + CtClass c = params[i]; + int size; + if (c instanceof CtPrimitiveType) + size = ((CtPrimitiveType)c).getDataSize(); + else + size = 1; + + storeStack0(i + 1, n, params, regno + size, bytecode); + bytecode.addStore(regno, c); + } + } + + // The implementation of replace() should call thisClass.checkModify() + // so that isModify() will return true. Otherwise, thisClass.classfile + // might be released during compilation and the compiler might generate + // bytecode with a wrong copy of ConstPool. + + /** + * Replaces this expression with the bytecode derived from + * the given source text. + * + * @param statement a Java statement except try-catch. + */ + public abstract void replace(String statement) throws CannotCompileException; + + /** + * Replaces this expression with the bytecode derived from + * the given source text and ExprEditor. + * + * @param statement a Java statement except try-catch. + * @param recursive if not null, the substituted bytecode + * is recursively processed by the given + * ExprEditor. + * @since 3.1 + */ + public void replace(String statement, ExprEditor recursive) + throws CannotCompileException + { + replace(statement); + if (recursive != null) + runEditor(recursive, iterator); + } + + protected void replace0(int pos, Bytecode bytecode, int size) + throws BadBytecode { + byte[] code = bytecode.get(); + edited = true; + int gap = code.length - size; + for (int i = 0; i < size; ++i) + iterator.writeByte(NOP, pos + i); + + if (gap > 0) + pos = iterator.insertGapAt(pos, gap, false).position; + + iterator.write(code, pos); + iterator.insert(bytecode.getExceptionTable(), pos); + maxLocals = bytecode.getMaxLocals(); + maxStack = bytecode.getMaxStack(); + } + + protected void runEditor(ExprEditor ed, CodeIterator oldIterator) + throws CannotCompileException + { + CodeAttribute codeAttr = oldIterator.get(); + int orgLocals = codeAttr.getMaxLocals(); + int orgStack = codeAttr.getMaxStack(); + int newLocals = locals(); + codeAttr.setMaxStack(stack()); + codeAttr.setMaxLocals(newLocals); + ExprEditor.LoopContext context + = new ExprEditor.LoopContext(newLocals); + int size = oldIterator.getCodeLength(); + int endPos = oldIterator.lookAhead(); + oldIterator.move(currentPos); + if (ed.doit(thisClass, thisMethod, context, oldIterator, endPos)) + edited = true; + + oldIterator.move(endPos + oldIterator.getCodeLength() - size); + codeAttr.setMaxLocals(orgLocals); + codeAttr.setMaxStack(orgStack); + maxLocals = context.maxLocals; + maxStack += context.maxStack; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/ExprEditor.java b/src/main/java/com/wenshuo/agent/javassist/expr/ExprEditor.java new file mode 100644 index 0000000..c4711bd --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/ExprEditor.java @@ -0,0 +1,317 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CannotCompileException; + +/** + * A translator of method bodies. + * + *

The users can define a subclass of this class to customize how to + * modify a method body. The overall architecture is similar to the + * strategy pattern. + * + *

If instrument() is called in + * CtMethod, the method body is scanned from the beginning + * to the end. + * Whenever an expression, such as a method call and a new + * expression (object creation), + * is found, edit() is called in ExprEdit. + * edit() can inspect and modify the given expression. + * The modification is reflected on the original method body. If + * edit() does nothing, the original method body is not + * changed. + * + *

The following code is an example: + * + *

+ * CtMethod cm = ...;
+ * cm.instrument(new ExprEditor() {
+ *     public void edit(MethodCall m) throws CannotCompileException {
+ *         if (m.getClassName().equals("Point")) {
+ *             System.out.println(m.getMethodName() + " line: "
+ *                                + m.getLineNumber());
+ *     }
+ * });
+ * 
+ * + *

This code inspects all method calls appearing in the method represented + * by cm and it prints the names and the line numbers of the + * methods declared in class Point. This code does not modify + * the body of the method represented by cm. If the method + * body must be modified, call replace() + * in MethodCall. + * + * @see javassist.CtClass#instrument(ExprEditor) + * @see javassist.CtMethod#instrument(ExprEditor) + * @see javassist.CtConstructor#instrument(ExprEditor) + * @see MethodCall + * @see NewExpr + * @see FieldAccess + * + * @see javassist.CodeConverter + */ +public class ExprEditor { + /** + * Default constructor. It does nothing. + */ + public ExprEditor() {} + + /** + * Undocumented method. Do not use; internal-use only. + */ + public boolean doit(CtClass clazz, MethodInfo minfo) + throws CannotCompileException + { + CodeAttribute codeAttr = minfo.getCodeAttribute(); + if (codeAttr == null) + return false; + + CodeIterator iterator = codeAttr.iterator(); + boolean edited = false; + LoopContext context = new LoopContext(codeAttr.getMaxLocals()); + + while (iterator.hasNext()) + if (loopBody(iterator, clazz, minfo, context)) + edited = true; + + ExceptionTable et = codeAttr.getExceptionTable(); + int n = et.size(); + for (int i = 0; i < n; ++i) { + Handler h = new Handler(et, i, iterator, clazz, minfo); + edit(h); + if (h.edited()) { + edited = true; + context.updateMax(h.locals(), h.stack()); + } + } + + // codeAttr might be modified by other partiess + // so I check the current value of max-locals. + if (codeAttr.getMaxLocals() < context.maxLocals) + codeAttr.setMaxLocals(context.maxLocals); + + codeAttr.setMaxStack(codeAttr.getMaxStack() + context.maxStack); + try { + if (edited) + minfo.rebuildStackMapIf6(clazz.getClassPool(), + clazz.getClassFile2()); + } + catch (BadBytecode b) { + throw new CannotCompileException(b.getMessage(), b); + } + + return edited; + } + + /** + * Visits each bytecode in the given range. + */ + boolean doit(CtClass clazz, MethodInfo minfo, LoopContext context, + CodeIterator iterator, int endPos) + throws CannotCompileException + { + boolean edited = false; + while (iterator.hasNext() && iterator.lookAhead() < endPos) { + int size = iterator.getCodeLength(); + if (loopBody(iterator, clazz, minfo, context)) { + edited = true; + int size2 = iterator.getCodeLength(); + if (size != size2) // the body was modified. + endPos += size2 - size; + } + } + + return edited; + } + + final static class NewOp { + NewOp next; + int pos; + String type; + + NewOp(NewOp n, int p, String t) { + next = n; + pos = p; + type = t; + } + } + + final static class LoopContext { + NewOp newList; + int maxLocals; + int maxStack; + + LoopContext(int locals) { + maxLocals = locals; + maxStack = 0; + newList = null; + } + + void updateMax(int locals, int stack) { + if (maxLocals < locals) + maxLocals = locals; + + if (maxStack < stack) + maxStack = stack; + } + } + + final boolean loopBody(CodeIterator iterator, CtClass clazz, + MethodInfo minfo, LoopContext context) + throws CannotCompileException + { + try { + Expr expr = null; + int pos = iterator.next(); + int c = iterator.byteAt(pos); + + if (c < Opcode.GETSTATIC) // c < 178 + /* skip */; + else if (c < Opcode.NEWARRAY) { // c < 188 + if (c == Opcode.INVOKESTATIC + || c == Opcode.INVOKEINTERFACE + || c == Opcode.INVOKEVIRTUAL) { + expr = new MethodCall(pos, iterator, clazz, minfo); + edit((MethodCall)expr); + } + else if (c == Opcode.GETFIELD || c == Opcode.GETSTATIC + || c == Opcode.PUTFIELD + || c == Opcode.PUTSTATIC) { + expr = new FieldAccess(pos, iterator, clazz, minfo, c); + edit((FieldAccess)expr); + } + else if (c == Opcode.NEW) { + int index = iterator.u16bitAt(pos + 1); + context.newList = new NewOp(context.newList, pos, + minfo.getConstPool().getClassInfo(index)); + } + else if (c == Opcode.INVOKESPECIAL) { + NewOp newList = context.newList; + if (newList != null + && minfo.getConstPool().isConstructor(newList.type, + iterator.u16bitAt(pos + 1)) > 0) { + expr = new NewExpr(pos, iterator, clazz, minfo, + newList.type, newList.pos); + edit((NewExpr)expr); + context.newList = newList.next; + } + else { + MethodCall mcall = new MethodCall(pos, iterator, clazz, minfo); + if (mcall.getMethodName().equals(MethodInfo.nameInit)) { + ConstructorCall ccall = new ConstructorCall(pos, iterator, clazz, minfo); + expr = ccall; + edit(ccall); + } + else { + expr = mcall; + edit(mcall); + } + } + } + } + else { // c >= 188 + if (c == Opcode.NEWARRAY || c == Opcode.ANEWARRAY + || c == Opcode.MULTIANEWARRAY) { + expr = new NewArray(pos, iterator, clazz, minfo, c); + edit((NewArray)expr); + } + else if (c == Opcode.INSTANCEOF) { + expr = new Instanceof(pos, iterator, clazz, minfo); + edit((Instanceof)expr); + } + else if (c == Opcode.CHECKCAST) { + expr = new Cast(pos, iterator, clazz, minfo); + edit((Cast)expr); + } + } + + if (expr != null && expr.edited()) { + context.updateMax(expr.locals(), expr.stack()); + return true; + } + else + return false; + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + /** + * Edits a new expression (overridable). + * The default implementation performs nothing. + * + * @param e the new expression creating an object. + */ + public void edit(NewExpr e) throws CannotCompileException {} + + /** + * Edits an expression for array creation (overridable). + * The default implementation performs nothing. + * + * @param a the new expression for creating an array. + * @throws CannotCompileException + */ + public void edit(NewArray a) throws CannotCompileException {} + + /** + * Edits a method call (overridable). + * + * The default implementation performs nothing. + */ + public void edit(MethodCall m) throws CannotCompileException {} + + /** + * Edits a constructor call (overridable). + * The constructor call is either + * super() or this() + * included in a constructor body. + * + * The default implementation performs nothing. + * + * @see #edit(NewExpr) + */ + public void edit(ConstructorCall c) throws CannotCompileException {} + + /** + * Edits a field-access expression (overridable). + * Field access means both read and write. + * The default implementation performs nothing. + */ + public void edit(FieldAccess f) throws CannotCompileException {} + + /** + * Edits an instanceof expression (overridable). + * The default implementation performs nothing. + */ + public void edit(Instanceof i) throws CannotCompileException {} + + /** + * Edits an expression for explicit type casting (overridable). + * The default implementation performs nothing. + */ + public void edit(Cast c) throws CannotCompileException {} + + /** + * Edits a catch clause (overridable). + * The default implementation performs nothing. + */ + public void edit(Handler h) throws CannotCompileException {} +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/FieldAccess.java b/src/main/java/com/wenshuo/agent/javassist/expr/FieldAccess.java new file mode 100644 index 0000000..523277e --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/FieldAccess.java @@ -0,0 +1,325 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.*; +import com.wenshuo.agent.javassist.compiler.ast.ASTList; + +/** + * Expression for accessing a field. + */ +public class FieldAccess extends Expr { + int opcode; + + protected FieldAccess(int pos, CodeIterator i, CtClass declaring, + MethodInfo m, int op) { + super(pos, i, declaring, m); + opcode = op; + } + + /** + * Returns the method or constructor containing the field-access + * expression represented by this object. + */ + public CtBehavior where() { return super.where(); } + + /** + * Returns the line number of the source line containing the + * field access. + * + * @return -1 if this information is not available. + */ + public int getLineNumber() { + return super.getLineNumber(); + } + + /** + * Returns the source file containing the field access. + * + * @return null if this information is not available. + */ + public String getFileName() { + return super.getFileName(); + } + + /** + * Returns true if the field is static. + */ + public boolean isStatic() { + return isStatic(opcode); + } + + static boolean isStatic(int c) { + return c == Opcode.GETSTATIC || c == Opcode.PUTSTATIC; + } + + /** + * Returns true if the field is read. + */ + public boolean isReader() { + return opcode == Opcode.GETFIELD || opcode == Opcode.GETSTATIC; + } + + /** + * Returns true if the field is written in. + */ + public boolean isWriter() { + return opcode == Opcode.PUTFIELD || opcode == Opcode.PUTSTATIC; + } + + /** + * Returns the class in which the field is declared. + */ + private CtClass getCtClass() throws NotFoundException { + return thisClass.getClassPool().get(getClassName()); + } + + /** + * Returns the name of the class in which the field is declared. + */ + public String getClassName() { + int index = iterator.u16bitAt(currentPos + 1); + return getConstPool().getFieldrefClassName(index); + } + + /** + * Returns the name of the field. + */ + public String getFieldName() { + int index = iterator.u16bitAt(currentPos + 1); + return getConstPool().getFieldrefName(index); + } + + /** + * Returns the field accessed by this expression. + */ + public CtField getField() throws NotFoundException { + CtClass cc = getCtClass(); + int index = iterator.u16bitAt(currentPos + 1); + ConstPool cp = getConstPool(); + return cc.getField(cp.getFieldrefName(index), cp.getFieldrefType(index)); + } + + /** + * Returns the list of exceptions that the expression may throw. + * This list includes both the exceptions that the try-catch statements + * including the expression can catch and the exceptions that + * the throws declaration allows the method to throw. + */ + public CtClass[] mayThrow() { + return super.mayThrow(); + } + + /** + * Returns the signature of the field type. + * The signature is represented by a character string + * called field descriptor, which is defined in the JVM specification. + * + * @see javassist.bytecode.Descriptor#toCtClass(String, ClassPool) + * @since 3.1 + */ + public String getSignature() { + int index = iterator.u16bitAt(currentPos + 1); + return getConstPool().getFieldrefType(index); + } + + /** + * Replaces the method call with the bytecode derived from + * the given source text. + * + *

$0 is available even if the called method is static. + * If the field access is writing, $_ is available but the value + * of $_ is ignored. + * + * @param statement a Java statement except try-catch. + */ + public void replace(String statement) throws CannotCompileException { + thisClass.getClassFile(); // to call checkModify(). + ConstPool constPool = getConstPool(); + int pos = currentPos; + int index = iterator.u16bitAt(pos + 1); + + Javac jc = new Javac(thisClass); + CodeAttribute ca = iterator.get(); + try { + CtClass[] params; + CtClass retType; + CtClass fieldType + = Descriptor.toCtClass(constPool.getFieldrefType(index), + thisClass.getClassPool()); + boolean read = isReader(); + if (read) { + params = new CtClass[0]; + retType = fieldType; + } + else { + params = new CtClass[1]; + params[0] = fieldType; + retType = CtClass.voidType; + } + + int paramVar = ca.getMaxLocals(); + jc.recordParams(constPool.getFieldrefClassName(index), params, + true, paramVar, withinStatic()); + + /* Is $_ included in the source code? + */ + boolean included = checkResultValue(retType, statement); + if (read) + included = true; + + int retVar = jc.recordReturnType(retType, included); + if (read) + jc.recordProceed(new ProceedForRead(retType, opcode, + index, paramVar)); + else { + // because $type is not the return type... + jc.recordType(fieldType); + jc.recordProceed(new ProceedForWrite(params[0], opcode, + index, paramVar)); + } + + Bytecode bytecode = jc.getBytecode(); + storeStack(params, isStatic(), paramVar, bytecode); + jc.recordLocalVariables(ca, pos); + + if (included) + if (retType == CtClass.voidType) { + bytecode.addOpcode(ACONST_NULL); + bytecode.addAstore(retVar); + } + else { + bytecode.addConstZero(retType); + bytecode.addStore(retVar, retType); // initialize $_ + } + + jc.compileStmnt(statement); + if (read) + bytecode.addLoad(retVar, retType); + + replace0(pos, bytecode, 3); + } + catch (CompileError e) { throw new CannotCompileException(e); } + catch (NotFoundException e) { throw new CannotCompileException(e); } + catch (BadBytecode e) { + throw new CannotCompileException("broken method"); + } + } + + /* $proceed() + */ + static class ProceedForRead implements ProceedHandler { + CtClass fieldType; + int opcode; + int targetVar, index; + + ProceedForRead(CtClass type, int op, int i, int var) { + fieldType = type; + targetVar = var; + opcode = op; + index = i; + } + + public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) + throws CompileError + { + if (args != null && !gen.isParamListName(args)) + throw new CompileError(Javac.proceedName + + "() cannot take a parameter for field reading"); + + int stack; + if (isStatic(opcode)) + stack = 0; + else { + stack = -1; + bytecode.addAload(targetVar); + } + + if (fieldType instanceof CtPrimitiveType) + stack += ((CtPrimitiveType)fieldType).getDataSize(); + else + ++stack; + + bytecode.add(opcode); + bytecode.addIndex(index); + bytecode.growStack(stack); + gen.setType(fieldType); + } + + public void setReturnType(JvstTypeChecker c, ASTList args) + throws CompileError + { + c.setType(fieldType); + } + } + + /* void $proceed() + * the return type is not the field type but void. + */ + static class ProceedForWrite implements ProceedHandler { + CtClass fieldType; + int opcode; + int targetVar, index; + + ProceedForWrite(CtClass type, int op, int i, int var) { + fieldType = type; + targetVar = var; + opcode = op; + index = i; + } + + public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) + throws CompileError + { + if (gen.getMethodArgsLength(args) != 1) + throw new CompileError(Javac.proceedName + + "() cannot take more than one parameter " + + "for field writing"); + + int stack; + if (isStatic(opcode)) + stack = 0; + else { + stack = -1; + bytecode.addAload(targetVar); + } + + gen.atMethodArgs(args, new int[1], new int[1], new String[1]); + gen.doNumCast(fieldType); + if (fieldType instanceof CtPrimitiveType) + stack -= ((CtPrimitiveType)fieldType).getDataSize(); + else + --stack; + + bytecode.add(opcode); + bytecode.addIndex(index); + bytecode.growStack(stack); + gen.setType(CtClass.voidType); + gen.addNullIfVoid(); + } + + public void setReturnType(JvstTypeChecker c, ASTList args) + throws CompileError + { + c.atMethodArgs(args, new int[1], new int[1], new String[1]); + c.setType(CtClass.voidType); + c.addNullIfVoid(); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/Handler.java b/src/main/java/com/wenshuo/agent/javassist/expr/Handler.java new file mode 100644 index 0000000..66665c3 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/Handler.java @@ -0,0 +1,146 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.*; + +/** + * A catch clause or a finally block. + */ +public class Handler extends Expr { + private static String EXCEPTION_NAME = "$1"; + private ExceptionTable etable; + private int index; + + /** + * Undocumented constructor. Do not use; internal-use only. + */ + protected Handler(ExceptionTable et, int nth, + CodeIterator it, CtClass declaring, MethodInfo m) { + super(et.handlerPc(nth), it, declaring, m); + etable = et; + index = nth; + } + + /** + * Returns the method or constructor containing the catch clause. + */ + public CtBehavior where() { return super.where(); } + + /** + * Returns the source line number of the catch clause. + * + * @return -1 if this information is not available. + */ + public int getLineNumber() { + return super.getLineNumber(); + } + + /** + * Returns the source file containing the catch clause. + * + * @return null if this information is not available. + */ + public String getFileName() { + return super.getFileName(); + } + + /** + * Returns the list of exceptions that the catch clause may throw. + */ + public CtClass[] mayThrow() { + return super.mayThrow(); + } + + /** + * Returns the type handled by the catch clause. + * If this is a finally block, null is returned. + */ + public CtClass getType() throws NotFoundException { + int type = etable.catchType(index); + if (type == 0) + return null; + else { + ConstPool cp = getConstPool(); + String name = cp.getClassInfo(type); + return thisClass.getClassPool().getCtClass(name); + } + } + + /** + * Returns true if this is a finally block. + */ + public boolean isFinally() { + return etable.catchType(index) == 0; + } + + /** + * This method has not been implemented yet. + * + * @param statement a Java statement except try-catch. + */ + public void replace(String statement) throws CannotCompileException { + throw new RuntimeException("not implemented yet"); + } + + /** + * Inserts bytecode at the beginning of the catch clause. + * The caught exception is stored in $1. + * + * @param src the source code representing the inserted bytecode. + * It must be a single statement or block. + */ + public void insertBefore(String src) throws CannotCompileException { + edited = true; + + ConstPool cp = getConstPool(); + CodeAttribute ca = iterator.get(); + Javac jv = new Javac(thisClass); + Bytecode b = jv.getBytecode(); + b.setStackDepth(1); + b.setMaxLocals(ca.getMaxLocals()); + + try { + CtClass type = getType(); + int var = jv.recordVariable(type, EXCEPTION_NAME); + jv.recordReturnType(type, false); + b.addAstore(var); + jv.compileStmnt(src); + b.addAload(var); + + int oldHandler = etable.handlerPc(index); + b.addOpcode(Opcode.GOTO); + b.addIndex(oldHandler - iterator.getCodeLength() + - b.currentPc() + 1); + + maxStack = b.getMaxStack(); + maxLocals = b.getMaxLocals(); + + int pos = iterator.append(b.get()); + iterator.append(b.getExceptionTable(), pos); + etable.setHandlerPc(index, pos); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (CompileError e) { + throw new CannotCompileException(e); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/Instanceof.java b/src/main/java/com/wenshuo/agent/javassist/expr/Instanceof.java new file mode 100644 index 0000000..1ea4ee1 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/Instanceof.java @@ -0,0 +1,170 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.*; +import com.wenshuo.agent.javassist.compiler.ast.ASTList; + +/** + * Instanceof operator. + */ +public class Instanceof extends Expr { + /** + * Undocumented constructor. Do not use; internal-use only. + */ + protected Instanceof(int pos, CodeIterator i, CtClass declaring, + MethodInfo m) { + super(pos, i, declaring, m); + } + + /** + * Returns the method or constructor containing the instanceof + * expression represented by this object. + */ + public CtBehavior where() { return super.where(); } + + /** + * Returns the line number of the source line containing the + * instanceof expression. + * + * @return -1 if this information is not available. + */ + public int getLineNumber() { + return super.getLineNumber(); + } + + /** + * Returns the source file containing the + * instanceof expression. + * + * @return null if this information is not available. + */ + public String getFileName() { + return super.getFileName(); + } + + /** + * Returns the CtClass object representing + * the type name on the right hand side + * of the instanceof operator. + */ + public CtClass getType() throws NotFoundException { + ConstPool cp = getConstPool(); + int pos = currentPos; + int index = iterator.u16bitAt(pos + 1); + String name = cp.getClassInfo(index); + return thisClass.getClassPool().getCtClass(name); + } + + /** + * Returns the list of exceptions that the expression may throw. + * This list includes both the exceptions that the try-catch statements + * including the expression can catch and the exceptions that + * the throws declaration allows the method to throw. + */ + public CtClass[] mayThrow() { + return super.mayThrow(); + } + + /** + * Replaces the instanceof operator with the bytecode derived from + * the given source text. + * + *

$0 is available but the value is null. + * + * @param statement a Java statement except try-catch. + */ + public void replace(String statement) throws CannotCompileException { + thisClass.getClassFile(); // to call checkModify(). + ConstPool constPool = getConstPool(); + int pos = currentPos; + int index = iterator.u16bitAt(pos + 1); + + Javac jc = new Javac(thisClass); + ClassPool cp = thisClass.getClassPool(); + CodeAttribute ca = iterator.get(); + + try { + CtClass[] params + = new CtClass[] { cp.get(javaLangObject) }; + CtClass retType = CtClass.booleanType; + + int paramVar = ca.getMaxLocals(); + jc.recordParams(javaLangObject, params, true, paramVar, + withinStatic()); + int retVar = jc.recordReturnType(retType, true); + jc.recordProceed(new ProceedForInstanceof(index)); + + // because $type is not the return type... + jc.recordType(getType()); + + /* Is $_ included in the source code? + */ + checkResultValue(retType, statement); + + Bytecode bytecode = jc.getBytecode(); + storeStack(params, true, paramVar, bytecode); + jc.recordLocalVariables(ca, pos); + + bytecode.addConstZero(retType); + bytecode.addStore(retVar, retType); // initialize $_ + + jc.compileStmnt(statement); + bytecode.addLoad(retVar, retType); + + replace0(pos, bytecode, 3); + } + catch (CompileError e) { throw new CannotCompileException(e); } + catch (NotFoundException e) { throw new CannotCompileException(e); } + catch (BadBytecode e) { + throw new CannotCompileException("broken method"); + } + } + + /* boolean $proceed(Object obj) + */ + static class ProceedForInstanceof implements ProceedHandler { + int index; + + ProceedForInstanceof(int i) { + index = i; + } + + public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) + throws CompileError + { + if (gen.getMethodArgsLength(args) != 1) + throw new CompileError(Javac.proceedName + + "() cannot take more than one parameter " + + "for instanceof"); + + gen.atMethodArgs(args, new int[1], new int[1], new String[1]); + bytecode.addOpcode(Opcode.INSTANCEOF); + bytecode.addIndex(index); + gen.setType(CtClass.booleanType); + } + + public void setReturnType(JvstTypeChecker c, ASTList args) + throws CompileError + { + c.atMethodArgs(args, new int[1], new int[1], new String[1]); + c.setType(CtClass.booleanType); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/MethodCall.java b/src/main/java/com/wenshuo/agent/javassist/expr/MethodCall.java new file mode 100644 index 0000000..70496b1 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/MethodCall.java @@ -0,0 +1,247 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.*; + +/** + * Method invocation (caller-side expression). + */ +public class MethodCall extends Expr { + /** + * Undocumented constructor. Do not use; internal-use only. + */ + protected MethodCall(int pos, CodeIterator i, CtClass declaring, + MethodInfo m) { + super(pos, i, declaring, m); + } + + private int getNameAndType(ConstPool cp) { + int pos = currentPos; + int c = iterator.byteAt(pos); + int index = iterator.u16bitAt(pos + 1); + + if (c == INVOKEINTERFACE) + return cp.getInterfaceMethodrefNameAndType(index); + else + return cp.getMethodrefNameAndType(index); + } + + /** + * Returns the method or constructor containing the method-call + * expression represented by this object. + */ + public CtBehavior where() { return super.where(); } + + /** + * Returns the line number of the source line containing the + * method call. + * + * @return -1 if this information is not available. + */ + public int getLineNumber() { + return super.getLineNumber(); + } + + /** + * Returns the source file containing the method call. + * + * @return null if this information is not available. + */ + public String getFileName() { + return super.getFileName(); + } + + /** + * Returns the class of the target object, + * which the method is called on. + */ + protected CtClass getCtClass() throws NotFoundException { + return thisClass.getClassPool().get(getClassName()); + } + + /** + * Returns the class name of the target object, + * which the method is called on. + */ + public String getClassName() { + String cname; + + ConstPool cp = getConstPool(); + int pos = currentPos; + int c = iterator.byteAt(pos); + int index = iterator.u16bitAt(pos + 1); + + if (c == INVOKEINTERFACE) + cname = cp.getInterfaceMethodrefClassName(index); + else + cname = cp.getMethodrefClassName(index); + + if (cname.charAt(0) == '[') + cname = Descriptor.toClassName(cname); + + return cname; + } + + /** + * Returns the name of the called method. + */ + public String getMethodName() { + ConstPool cp = getConstPool(); + int nt = getNameAndType(cp); + return cp.getUtf8Info(cp.getNameAndTypeName(nt)); + } + + /** + * Returns the called method. + */ + public CtMethod getMethod() throws NotFoundException { + return getCtClass().getMethod(getMethodName(), getSignature()); + } + + /** + * Returns the method signature (the parameter types + * and the return type). + * The method signature is represented by a character string + * called method descriptor, which is defined in the JVM specification. + * + * @see javassist.CtBehavior#getSignature() + * @see javassist.bytecode.Descriptor + * @since 3.1 + */ + public String getSignature() { + ConstPool cp = getConstPool(); + int nt = getNameAndType(cp); + return cp.getUtf8Info(cp.getNameAndTypeDescriptor(nt)); + } + + /** + * Returns the list of exceptions that the expression may throw. + * This list includes both the exceptions that the try-catch statements + * including the expression can catch and the exceptions that + * the throws declaration allows the method to throw. + */ + public CtClass[] mayThrow() { + return super.mayThrow(); + } + + /** + * Returns true if the called method is of a superclass of the current + * class. + */ + public boolean isSuper() { + return iterator.byteAt(currentPos) == INVOKESPECIAL + && !where().getDeclaringClass().getName().equals(getClassName()); + } + + /* + * Returns the parameter types of the called method. + + public CtClass[] getParameterTypes() throws NotFoundException { + return Descriptor.getParameterTypes(getMethodDesc(), + thisClass.getClassPool()); + } + */ + + /* + * Returns the return type of the called method. + + public CtClass getReturnType() throws NotFoundException { + return Descriptor.getReturnType(getMethodDesc(), + thisClass.getClassPool()); + } + */ + + /** + * Replaces the method call with the bytecode derived from + * the given source text. + * + *

$0 is available even if the called method is static. + * + * @param statement a Java statement except try-catch. + */ + public void replace(String statement) throws CannotCompileException { + thisClass.getClassFile(); // to call checkModify(). + ConstPool constPool = getConstPool(); + int pos = currentPos; + int index = iterator.u16bitAt(pos + 1); + + String classname, methodname, signature; + int opcodeSize; + int c = iterator.byteAt(pos); + if (c == INVOKEINTERFACE) { + opcodeSize = 5; + classname = constPool.getInterfaceMethodrefClassName(index); + methodname = constPool.getInterfaceMethodrefName(index); + signature = constPool.getInterfaceMethodrefType(index); + } + else if (c == INVOKESTATIC + || c == INVOKESPECIAL || c == INVOKEVIRTUAL) { + opcodeSize = 3; + classname = constPool.getMethodrefClassName(index); + methodname = constPool.getMethodrefName(index); + signature = constPool.getMethodrefType(index); + } + else + throw new CannotCompileException("not method invocation"); + + Javac jc = new Javac(thisClass); + ClassPool cp = thisClass.getClassPool(); + CodeAttribute ca = iterator.get(); + try { + CtClass[] params = Descriptor.getParameterTypes(signature, cp); + CtClass retType = Descriptor.getReturnType(signature, cp); + int paramVar = ca.getMaxLocals(); + jc.recordParams(classname, params, + true, paramVar, withinStatic()); + int retVar = jc.recordReturnType(retType, true); + if (c == INVOKESTATIC) + jc.recordStaticProceed(classname, methodname); + else if (c == INVOKESPECIAL) + jc.recordSpecialProceed(Javac.param0Name, classname, + methodname, signature); + else + jc.recordProceed(Javac.param0Name, methodname); + + /* Is $_ included in the source code? + */ + checkResultValue(retType, statement); + + Bytecode bytecode = jc.getBytecode(); + storeStack(params, c == INVOKESTATIC, paramVar, bytecode); + jc.recordLocalVariables(ca, pos); + + if (retType != CtClass.voidType) { + bytecode.addConstZero(retType); + bytecode.addStore(retVar, retType); // initialize $_ + } + + jc.compileStmnt(statement); + if (retType != CtClass.voidType) + bytecode.addLoad(retVar, retType); + + replace0(pos, bytecode, opcodeSize); + } + catch (CompileError e) { throw new CannotCompileException(e); } + catch (NotFoundException e) { throw new CannotCompileException(e); } + catch (BadBytecode e) { + throw new CannotCompileException("broken method"); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/NewArray.java b/src/main/java/com/wenshuo/agent/javassist/expr/NewArray.java new file mode 100644 index 0000000..1c8f1e6 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/NewArray.java @@ -0,0 +1,283 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.*; +import com.wenshuo.agent.javassist.compiler.ast.ASTList; + +/** + * Array creation. + * + *

This class does not provide methods for obtaining the initial + * values of array elements. + */ +public class NewArray extends Expr { + int opcode; + + protected NewArray(int pos, CodeIterator i, CtClass declaring, + MethodInfo m, int op) { + super(pos, i, declaring, m); + opcode = op; + } + + /** + * Returns the method or constructor containing the array creation + * represented by this object. + */ + public CtBehavior where() { return super.where(); } + + /** + * Returns the line number of the source line containing the + * array creation. + * + * @return -1 if this information is not available. + */ + public int getLineNumber() { + return super.getLineNumber(); + } + + /** + * Returns the source file containing the array creation. + * + * @return null if this information is not available. + */ + public String getFileName() { + return super.getFileName(); + } + + /** + * Returns the list of exceptions that the expression may throw. + * This list includes both the exceptions that the try-catch statements + * including the expression can catch and the exceptions that + * the throws declaration allows the method to throw. + */ + public CtClass[] mayThrow() { + return super.mayThrow(); + } + + /** + * Returns the type of array components. If the created array is + * a two-dimensional array of int, + * the type returned by this method is + * not int[] but int. + */ + public CtClass getComponentType() throws NotFoundException { + if (opcode == Opcode.NEWARRAY) { + int atype = iterator.byteAt(currentPos + 1); + return getPrimitiveType(atype); + } + else if (opcode == Opcode.ANEWARRAY + || opcode == Opcode.MULTIANEWARRAY) { + int index = iterator.u16bitAt(currentPos + 1); + String desc = getConstPool().getClassInfo(index); + int dim = Descriptor.arrayDimension(desc); + desc = Descriptor.toArrayComponent(desc, dim); + return Descriptor.toCtClass(desc, thisClass.getClassPool()); + } + else + throw new RuntimeException("bad opcode: " + opcode); + } + + CtClass getPrimitiveType(int atype) { + switch (atype) { + case Opcode.T_BOOLEAN : + return CtClass.booleanType; + case Opcode.T_CHAR : + return CtClass.charType; + case Opcode.T_FLOAT : + return CtClass.floatType; + case Opcode.T_DOUBLE : + return CtClass.doubleType; + case Opcode.T_BYTE : + return CtClass.byteType; + case Opcode.T_SHORT : + return CtClass.shortType; + case Opcode.T_INT : + return CtClass.intType; + case Opcode.T_LONG : + return CtClass.longType; + default : + throw new RuntimeException("bad atype: " + atype); + } + } + + /** + * Returns the dimension of the created array. + */ + public int getDimension() { + if (opcode == Opcode.NEWARRAY) + return 1; + else if (opcode == Opcode.ANEWARRAY + || opcode == Opcode.MULTIANEWARRAY) { + int index = iterator.u16bitAt(currentPos + 1); + String desc = getConstPool().getClassInfo(index); + return Descriptor.arrayDimension(desc) + + (opcode == Opcode.ANEWARRAY ? 1 : 0); + } + else + throw new RuntimeException("bad opcode: " + opcode); + } + + /** + * Returns the number of dimensions of arrays to be created. + * If the opcode is multianewarray, this method returns the second + * operand. Otherwise, it returns 1. + */ + public int getCreatedDimensions() { + if (opcode == Opcode.MULTIANEWARRAY) + return iterator.byteAt(currentPos + 3); + else + return 1; + } + + /** + * Replaces the array creation with the bytecode derived from + * the given source text. + * + *

$0 is available even if the called method is static. + * If the field access is writing, $_ is available but the value + * of $_ is ignored. + * + * @param statement a Java statement except try-catch. + */ + public void replace(String statement) throws CannotCompileException { + try { + replace2(statement); + } + catch (CompileError e) { throw new CannotCompileException(e); } + catch (NotFoundException e) { throw new CannotCompileException(e); } + catch (BadBytecode e) { + throw new CannotCompileException("broken method"); + } + } + + private void replace2(String statement) + throws CompileError, NotFoundException, BadBytecode, + CannotCompileException + { + thisClass.getClassFile(); // to call checkModify(). + ConstPool constPool = getConstPool(); + int pos = currentPos; + CtClass retType; + int codeLength; + int index = 0; + int dim = 1; + String desc; + if (opcode == Opcode.NEWARRAY) { + index = iterator.byteAt(currentPos + 1); // atype + CtPrimitiveType cpt = (CtPrimitiveType)getPrimitiveType(index); + desc = "[" + cpt.getDescriptor(); + codeLength = 2; + } + else if (opcode == Opcode.ANEWARRAY) { + index = iterator.u16bitAt(pos + 1); + desc = constPool.getClassInfo(index); + if (desc.startsWith("[")) + desc = "[" + desc; + else + desc = "[L" + desc + ";"; + + codeLength = 3; + } + else if (opcode == Opcode.MULTIANEWARRAY) { + index = iterator.u16bitAt(currentPos + 1); + desc = constPool.getClassInfo(index); + dim = iterator.byteAt(currentPos + 3); + codeLength = 4; + } + else + throw new RuntimeException("bad opcode: " + opcode); + + retType = Descriptor.toCtClass(desc, thisClass.getClassPool()); + + Javac jc = new Javac(thisClass); + CodeAttribute ca = iterator.get(); + + CtClass[] params = new CtClass[dim]; + for (int i = 0; i < dim; ++i) + params[i] = CtClass.intType; + + int paramVar = ca.getMaxLocals(); + jc.recordParams(javaLangObject, params, + true, paramVar, withinStatic()); + + /* Is $_ included in the source code? + */ + checkResultValue(retType, statement); + int retVar = jc.recordReturnType(retType, true); + jc.recordProceed(new ProceedForArray(retType, opcode, index, dim)); + + Bytecode bytecode = jc.getBytecode(); + storeStack(params, true, paramVar, bytecode); + jc.recordLocalVariables(ca, pos); + + bytecode.addOpcode(ACONST_NULL); // initialize $_ + bytecode.addAstore(retVar); + + jc.compileStmnt(statement); + bytecode.addAload(retVar); + + replace0(pos, bytecode, codeLength); + } + + /* $proceed( ..) + */ + static class ProceedForArray implements ProceedHandler { + CtClass arrayType; + int opcode; + int index, dimension; + + ProceedForArray(CtClass type, int op, int i, int dim) { + arrayType = type; + opcode = op; + index = i; + dimension = dim; + } + + public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) + throws CompileError + { + int num = gen.getMethodArgsLength(args); + if (num != dimension) + throw new CompileError(Javac.proceedName + + "() with a wrong number of parameters"); + + gen.atMethodArgs(args, new int[num], + new int[num], new String[num]); + bytecode.addOpcode(opcode); + if (opcode == Opcode.ANEWARRAY) + bytecode.addIndex(index); + else if (opcode == Opcode.NEWARRAY) + bytecode.add(index); + else /* if (opcode == Opcode.MULTIANEWARRAY) */ { + bytecode.addIndex(index); + bytecode.add(dimension); + bytecode.growStack(1 - dimension); + } + + gen.setType(arrayType); + } + + public void setReturnType(JvstTypeChecker c, ASTList args) + throws CompileError + { + c.setType(arrayType); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/NewExpr.java b/src/main/java/com/wenshuo/agent/javassist/expr/NewExpr.java new file mode 100644 index 0000000..e3f1a14 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/NewExpr.java @@ -0,0 +1,249 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.expr; + +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.bytecode.*; +import com.wenshuo.agent.javassist.compiler.*; +import com.wenshuo.agent.javassist.compiler.ast.ASTList; + +/** + * Object creation (new expression). + */ +public class NewExpr extends Expr { + String newTypeName; + int newPos; + + /** + * Undocumented constructor. Do not use; internal-use only. + */ + protected NewExpr(int pos, CodeIterator i, CtClass declaring, + MethodInfo m, String type, int np) { + super(pos, i, declaring, m); + newTypeName = type; + newPos = np; + } + + /* + * Not used + * + private int getNameAndType(ConstPool cp) { + int pos = currentPos; + int c = iterator.byteAt(pos); + int index = iterator.u16bitAt(pos + 1); + + if (c == INVOKEINTERFACE) + return cp.getInterfaceMethodrefNameAndType(index); + else + return cp.getMethodrefNameAndType(index); + } */ + + /** + * Returns the method or constructor containing the new + * expression represented by this object. + */ + public CtBehavior where() { return super.where(); } + + /** + * Returns the line number of the source line containing the + * new expression. + * + * @return -1 if this information is not available. + */ + public int getLineNumber() { + return super.getLineNumber(); + } + + /** + * Returns the source file containing the new expression. + * + * @return null if this information is not available. + */ + public String getFileName() { + return super.getFileName(); + } + + /** + * Returns the class of the created object. + */ + private CtClass getCtClass() throws NotFoundException { + return thisClass.getClassPool().get(newTypeName); + } + + /** + * Returns the class name of the created object. + */ + public String getClassName() { + return newTypeName; + } + + /** + * Get the signature of the constructor + * + * The signature is represented by a character string + * called method descriptor, which is defined in the JVM specification. + * + * @see javassist.CtBehavior#getSignature() + * @see javassist.bytecode.Descriptor + * @return the signature + */ + public String getSignature() { + ConstPool constPool = getConstPool(); + int methodIndex = iterator.u16bitAt(currentPos + 1); // constructor + return constPool.getMethodrefType(methodIndex); + } + + /** + * Returns the constructor called for creating the object. + */ + public CtConstructor getConstructor() throws NotFoundException { + ConstPool cp = getConstPool(); + int index = iterator.u16bitAt(currentPos + 1); + String desc = cp.getMethodrefType(index); + return getCtClass().getConstructor(desc); + } + + /** + * Returns the list of exceptions that the expression may throw. + * This list includes both the exceptions that the try-catch statements + * including the expression can catch and the exceptions that + * the throws declaration allows the method to throw. + */ + public CtClass[] mayThrow() { + return super.mayThrow(); + } + + /* + * Returns the parameter types of the constructor. + + public CtClass[] getParameterTypes() throws NotFoundException { + ConstPool cp = getConstPool(); + int index = iterator.u16bitAt(currentPos + 1); + String desc = cp.getMethodrefType(index); + return Descriptor.getParameterTypes(desc, thisClass.getClassPool()); + } + */ + + private int canReplace() throws CannotCompileException { + int op = iterator.byteAt(newPos + 3); + if (op == Opcode.DUP) // Typical single DUP or Javaflow DUP DUP2_X2 POP2 + return ((iterator.byteAt(newPos + 4) == Opcode.DUP2_X2 + && iterator.byteAt(newPos + 5) == Opcode.POP2)) ? 6 : 4; + else if (op == Opcode.DUP_X1 + && iterator.byteAt(newPos + 4) == Opcode.SWAP) + return 5; + else + return 3; // for Eclipse. The generated code may include no DUP. + // throw new CannotCompileException( + // "sorry, cannot edit NEW followed by no DUP"); + } + + /** + * Replaces the new expression with the bytecode derived from + * the given source text. + * + *

$0 is available but the value is null. + * + * @param statement a Java statement except try-catch. + */ + public void replace(String statement) throws CannotCompileException { + thisClass.getClassFile(); // to call checkModify(). + + final int bytecodeSize = 3; + int pos = newPos; + + int newIndex = iterator.u16bitAt(pos + 1); + + /* delete the preceding NEW and DUP (or DUP_X1, SWAP) instructions. + */ + int codeSize = canReplace(); + int end = pos + codeSize; + for (int i = pos; i < end; ++i) + iterator.writeByte(NOP, i); + + ConstPool constPool = getConstPool(); + pos = currentPos; + int methodIndex = iterator.u16bitAt(pos + 1); // constructor + + String signature = constPool.getMethodrefType(methodIndex); + + Javac jc = new Javac(thisClass); + ClassPool cp = thisClass.getClassPool(); + CodeAttribute ca = iterator.get(); + try { + CtClass[] params = Descriptor.getParameterTypes(signature, cp); + CtClass newType = cp.get(newTypeName); + int paramVar = ca.getMaxLocals(); + jc.recordParams(newTypeName, params, + true, paramVar, withinStatic()); + int retVar = jc.recordReturnType(newType, true); + jc.recordProceed(new ProceedForNew(newType, newIndex, + methodIndex)); + + /* Is $_ included in the source code? + */ + checkResultValue(newType, statement); + + Bytecode bytecode = jc.getBytecode(); + storeStack(params, true, paramVar, bytecode); + jc.recordLocalVariables(ca, pos); + + bytecode.addConstZero(newType); + bytecode.addStore(retVar, newType); // initialize $_ + + jc.compileStmnt(statement); + if (codeSize > 3) // if the original code includes DUP. + bytecode.addAload(retVar); + + replace0(pos, bytecode, bytecodeSize); + } + catch (CompileError e) { throw new CannotCompileException(e); } + catch (NotFoundException e) { throw new CannotCompileException(e); } + catch (BadBytecode e) { + throw new CannotCompileException("broken method"); + } + } + + static class ProceedForNew implements ProceedHandler { + CtClass newType; + int newIndex, methodIndex; + + ProceedForNew(CtClass nt, int ni, int mi) { + newType = nt; + newIndex = ni; + methodIndex = mi; + } + + public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) + throws CompileError + { + bytecode.addOpcode(NEW); + bytecode.addIndex(newIndex); + bytecode.addOpcode(DUP); + gen.atMethodCallCore(newType, MethodInfo.nameInit, args, + false, true, -1, null); + gen.setType(newType); + } + + public void setReturnType(JvstTypeChecker c, ASTList args) + throws CompileError + { + c.atMethodCallCore(newType, MethodInfo.nameInit, args); + c.setType(newType); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/package.html b/src/main/java/com/wenshuo/agent/javassist/expr/package.html new file mode 100644 index 0000000..12a2c63 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/expr/package.html @@ -0,0 +1,8 @@ + + + +

This package contains the classes for modifying a method body. +See ExprEditor (expression editor) for more details. + + + diff --git a/src/main/java/com/wenshuo/agent/javassist/package.html b/src/main/java/com/wenshuo/agent/javassist/package.html new file mode 100644 index 0000000..f5b66b9 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/package.html @@ -0,0 +1,22 @@ + + +The Javassist Core API. + +

Javassist (Java programming assistant) makes bytecode +engineering simple. It is a class library for editing +bytecode in Java; it enables Java programs to define a new class at +runtime and to modify a given class file when the JVM loads it. + +

The most significant class of this package is CtClass. +See the description of this class first. + +

To know the version number of this package, type the following command: + +

    +java -jar javassist.jar
    +
+ +

It prints the version number on the console. + + + diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/Cflow.java b/src/main/java/com/wenshuo/agent/javassist/runtime/Cflow.java new file mode 100644 index 0000000..f2c7b6a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/runtime/Cflow.java @@ -0,0 +1,53 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.runtime; + +/** + * A support class for implementing $cflow. + * This support class is required at runtime + * only if $cflow is used. + * + * @see javassist.CtBehavior#useCflow(String) + */ +public class Cflow extends ThreadLocal { + private static class Depth { + private int depth; + Depth() { depth = 0; } + int get() { return depth; } + void inc() { ++depth; } + void dec() { --depth; } + } + + protected synchronized Object initialValue() { + return new Depth(); + } + + /** + * Increments the counter. + */ + public void enter() { ((Depth)get()).inc(); } + + /** + * Decrements the counter. + */ + public void exit() { ((Depth)get()).dec(); } + + /** + * Returns the value of the counter. + */ + public int value() { return ((Depth)get()).get(); } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/Desc.java b/src/main/java/com/wenshuo/agent/javassist/runtime/Desc.java new file mode 100644 index 0000000..e8ed8bf --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/runtime/Desc.java @@ -0,0 +1,161 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.runtime; + +/** + * A support class for implementing $sig and + * $type. + * This support class is required at runtime + * only if $sig or $type is used. + */ +public class Desc { + + /** + * Specifies how a java.lang.Class object is loaded. + * + *

If true, it is loaded by: + *

Thread.currentThread().getContextClassLoader().loadClass()
+ *

If false, it is loaded by Class.forName(). + * The default value is false. + */ + public static boolean useContextClassLoader = false; + + private static Class getClassObject(String name) + throws ClassNotFoundException + { + if (useContextClassLoader) + return Class.forName(name, true, Thread.currentThread().getContextClassLoader()); + else + return Class.forName(name); + } + + /** + * Interprets the given class name. + * It is used for implementing $class. + */ + public static Class getClazz(String name) { + try { + return getClassObject(name); + } + catch (ClassNotFoundException e) { + throw new RuntimeException( + "$class: internal error, could not find class '" + name + + "' (Desc.useContextClassLoader: " + + Boolean.toString(useContextClassLoader) + ")", e); + } + } + + /** + * Interprets the given type descriptor representing a method + * signature. It is used for implementing $sig. + */ + public static Class[] getParams(String desc) { + if (desc.charAt(0) != '(') + throw new RuntimeException("$sig: internal error"); + + return getType(desc, desc.length(), 1, 0); + } + + /** + * Interprets the given type descriptor. + * It is used for implementing $type. + */ + public static Class getType(String desc) { + Class[] result = getType(desc, desc.length(), 0, 0); + if (result == null || result.length != 1) + throw new RuntimeException("$type: internal error"); + + return result[0]; + } + + private static Class[] getType(String desc, int descLen, + int start, int num) { + Class clazz; + if (start >= descLen) + return new Class[num]; + + char c = desc.charAt(start); + switch (c) { + case 'Z' : + clazz = Boolean.TYPE; + break; + case 'C' : + clazz = Character.TYPE; + break; + case 'B' : + clazz = Byte.TYPE; + break; + case 'S' : + clazz = Short.TYPE; + break; + case 'I' : + clazz = Integer.TYPE; + break; + case 'J' : + clazz = Long.TYPE; + break; + case 'F' : + clazz = Float.TYPE; + break; + case 'D' : + clazz = Double.TYPE; + break; + case 'V' : + clazz = Void.TYPE; + break; + case 'L' : + case '[' : + return getClassType(desc, descLen, start, num); + default : + return new Class[num]; + } + + Class[] result = getType(desc, descLen, start + 1, num + 1); + result[num] = clazz; + return result; + } + + private static Class[] getClassType(String desc, int descLen, + int start, int num) { + int end = start; + while (desc.charAt(end) == '[') + ++end; + + if (desc.charAt(end) == 'L') { + end = desc.indexOf(';', end); + if (end < 0) + throw new IndexOutOfBoundsException("bad descriptor"); + } + + String cname; + if (desc.charAt(start) == 'L') + cname = desc.substring(start + 1, end); + else + cname = desc.substring(start, end + 1); + + Class[] result = getType(desc, descLen, end + 1, num + 1); + try { + result[num] = getClassObject(cname.replace('/', '.')); + } + catch (ClassNotFoundException e) { + // "new RuntimeException(e)" is not available in JDK 1.3. + throw new RuntimeException(e.getMessage()); + } + + return result; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/DotClass.java b/src/main/java/com/wenshuo/agent/javassist/runtime/DotClass.java new file mode 100644 index 0000000..e3adef4 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/runtime/DotClass.java @@ -0,0 +1,29 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.runtime; + +/** + * A support class for implementing .class notation. + * This is required at runtime + * only if .class notation is used in source code given + * to the Javassist compiler. + */ +public class DotClass { + public static NoClassDefFoundError fail(ClassNotFoundException e) { + return new NoClassDefFoundError(e.getMessage()); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/Inner.java b/src/main/java/com/wenshuo/agent/javassist/runtime/Inner.java new file mode 100644 index 0000000..e51ab88 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/runtime/Inner.java @@ -0,0 +1,25 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.runtime; + +/** + * A support class for compiling a method declared in an inner class. + * This support class is required at runtime + * only if the method calls a private constructor in the enclosing class. + */ +public class Inner { +} diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/package.html b/src/main/java/com/wenshuo/agent/javassist/runtime/package.html new file mode 100644 index 0000000..313648f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/runtime/package.html @@ -0,0 +1,12 @@ + + +Runtime support classes required by modified bytecode. + +

This package includes support classes that may be required by +classes modified with Javassist. Note that most of the modified +classes do not require these support classes. See the documentation +of every support class to know which kind of modification needs +a support class. + + + diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java new file mode 100644 index 0000000..ce0dbd8 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java @@ -0,0 +1,309 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.scopedpool; + +import java.lang.ref.WeakReference; +import java.security.ProtectionDomain; +import java.util.Iterator; +import java.util.Map; +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.LoaderClassPath; +import com.wenshuo.agent.javassist.NotFoundException; + +/** + * A scoped class pool. + * + * @author Bill Burke + * @author Adrian Brock + * @author Kabir Khan + * @version $Revision: 1.8 $ + */ +public class ScopedClassPool extends ClassPool { + protected ScopedClassPoolRepository repository; + + protected WeakReference classLoader; + + protected LoaderClassPath classPath; + + protected SoftValueHashMap softcache = new SoftValueHashMap(); + + boolean isBootstrapCl = true; + + static { + ClassPool.doPruning = false; + ClassPool.releaseUnmodifiedClassFile = false; + } + + /** + * Create a new ScopedClassPool. + * + * @param cl + * the classloader + * @param src + * the original class pool + * @param repository + * the repository + *@deprecated + */ + protected ScopedClassPool(ClassLoader cl, ClassPool src, + ScopedClassPoolRepository repository) { + this(cl, src, repository, false); + } + + /** + * Create a new ScopedClassPool. + * + * @param cl + * the classloader + * @param src + * the original class pool + * @param repository + * the repository + * @param isTemp + * Whether this is a temporary pool used to resolve references + */ + protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository, boolean isTemp) + { + super(src); + this.repository = repository; + this.classLoader = new WeakReference(cl); + if (cl != null) { + classPath = new LoaderClassPath(cl); + this.insertClassPath(classPath); + } + childFirstLookup = true; + if (!isTemp && cl == null) + { + isBootstrapCl = true; + } + } + + /** + * Get the class loader + * + * @return the class loader + */ + public ClassLoader getClassLoader() { + ClassLoader cl = getClassLoader0(); + if (cl == null && !isBootstrapCl) + { + throw new IllegalStateException( + "ClassLoader has been garbage collected"); + } + return cl; + } + + protected ClassLoader getClassLoader0() { + return (ClassLoader)classLoader.get(); + } + + /** + * Close the class pool + */ + public void close() { + this.removeClassPath(classPath); + classPath.close(); + classes.clear(); + softcache.clear(); + } + + /** + * Flush a class + * + * @param classname + * the class to flush + */ + public synchronized void flushClass(String classname) { + classes.remove(classname); + softcache.remove(classname); + } + + /** + * Soften a class + * + * @param clazz + * the class + */ + public synchronized void soften(CtClass clazz) { + if (repository.isPrune()) + clazz.prune(); + classes.remove(clazz.getName()); + softcache.put(clazz.getName(), clazz); + } + + /** + * Whether the classloader is loader + * + * @return false always + */ + public boolean isUnloadedClassLoader() { + return false; + } + + /** + * Get the cached class + * + * @param classname + * the class name + * @return the class + */ + protected CtClass getCached(String classname) { + CtClass clazz = getCachedLocally(classname); + if (clazz == null) { + boolean isLocal = false; + + ClassLoader dcl = getClassLoader0(); + if (dcl != null) { + final int lastIndex = classname.lastIndexOf('$'); + String classResourceName = null; + if (lastIndex < 0) { + classResourceName = classname.replaceAll("[\\.]", "/") + + ".class"; + } + else { + classResourceName = classname.substring(0, lastIndex) + .replaceAll("[\\.]", "/") + + classname.substring(lastIndex) + ".class"; + } + + isLocal = dcl.getResource(classResourceName) != null; + } + + if (!isLocal) { + Map registeredCLs = repository.getRegisteredCLs(); + synchronized (registeredCLs) { + Iterator it = registeredCLs.values().iterator(); + while (it.hasNext()) { + ScopedClassPool pool = (ScopedClassPool)it.next(); + if (pool.isUnloadedClassLoader()) { + repository.unregisterClassLoader(pool + .getClassLoader()); + continue; + } + + clazz = pool.getCachedLocally(classname); + if (clazz != null) { + return clazz; + } + } + } + } + } + // *NOTE* NEED TO TEST WHEN SUPERCLASS IS IN ANOTHER UCL!!!!!! + return clazz; + } + + /** + * Cache a class + * + * @param classname + * the class name + * @param c + * the ctClass + * @param dynamic + * whether the class is dynamically generated + */ + protected void cacheCtClass(String classname, CtClass c, boolean dynamic) { + if (dynamic) { + super.cacheCtClass(classname, c, dynamic); + } + else { + if (repository.isPrune()) + c.prune(); + softcache.put(classname, c); + } + } + + /** + * Lock a class into the cache + * + * @param c + * the class + */ + public void lockInCache(CtClass c) { + super.cacheCtClass(c.getName(), c, false); + } + + /** + * Whether the class is cached in this pooled + * + * @param classname + * the class name + * @return the cached class + */ + protected CtClass getCachedLocally(String classname) { + CtClass cached = (CtClass)classes.get(classname); + if (cached != null) + return cached; + synchronized (softcache) { + return (CtClass)softcache.get(classname); + } + } + + /** + * Get any local copy of the class + * + * @param classname + * the class name + * @return the class + * @throws NotFoundException + * when the class is not found + */ + public synchronized CtClass getLocally(String classname) + throws NotFoundException { + softcache.remove(classname); + CtClass clazz = (CtClass)classes.get(classname); + if (clazz == null) { + clazz = createCtClass(classname, true); + if (clazz == null) + throw new NotFoundException(classname); + super.cacheCtClass(classname, clazz, false); + } + + return clazz; + } + + /** + * Convert a javassist class to a java class + * + * @param ct + * the javassist class + * @param loader + * the loader + * @throws CannotCompileException + * for any error + */ + public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) + throws CannotCompileException { + // We need to pass up the classloader stored in this pool, as the + // default implementation uses the Thread context cl. + // In the case of JSP's in Tomcat, + // org.apache.jasper.servlet.JasperLoader will be stored here, while + // it's parent + // org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader is used as the Thread + // context cl. The invocation class needs to + // be generated in the JasperLoader classloader since in the case of + // method invocations, the package name will be + // the same as for the class generated from the jsp, i.e. + // org.apache.jsp. For classes belonging to org.apache.jsp, + // JasperLoader does NOT delegate to its parent if it cannot find them. + lockInCache(ct); + return super.toClass(ct, getClassLoader0(), domain); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java new file mode 100644 index 0000000..2dd05b4 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java @@ -0,0 +1,39 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.scopedpool; + +import com.wenshuo.agent.javassist.ClassPool; + +/** + * A factory interface. + * + * @author Kabir Khan + * @version $Revision: 1.4 $ + */ +public interface ScopedClassPoolFactory { + /** + * Makes an instance. + */ + ScopedClassPool create(ClassLoader cl, ClassPool src, + ScopedClassPoolRepository repository); + + /** + * Makes an instance. + */ + ScopedClassPool create(ClassPool src, + ScopedClassPoolRepository repository); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java new file mode 100644 index 0000000..1386fff --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java @@ -0,0 +1,43 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.scopedpool; + +import com.wenshuo.agent.javassist.ClassPool; + +/** + * An implementation of factory. + * + * @author Kabir Khan + * @version $Revision: 1.5 $ + */ +public class ScopedClassPoolFactoryImpl implements ScopedClassPoolFactory { + /** + * Makes an instance. + */ + public ScopedClassPool create(ClassLoader cl, ClassPool src, + ScopedClassPoolRepository repository) { + return new ScopedClassPool(cl, src, repository, false); + } + + /** + * Makes an instance. + */ + public ScopedClassPool create(ClassPool src, + ScopedClassPoolRepository repository) { + return new ScopedClassPool(null, src, repository, true); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java new file mode 100644 index 0000000..4965cff --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java @@ -0,0 +1,98 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.scopedpool; + +import java.util.Map; + +import com.wenshuo.agent.javassist.ClassPool; + +/** + * An interface to ScopedClassPoolRepositoryImpl. + * + * @author Kabir Khan + * @version $Revision: 1.4 $ + */ +public interface ScopedClassPoolRepository { + /** + * Records a factory. + */ + void setClassPoolFactory(ScopedClassPoolFactory factory); + + /** + * Obtains the recorded factory. + */ + ScopedClassPoolFactory getClassPoolFactory(); + + /** + * Returns whether or not the class pool is pruned. + * + * @return the prune. + */ + boolean isPrune(); + + /** + * Sets the prune flag. + * + * @param prune a new value. + */ + void setPrune(boolean prune); + + /** + * Create a scoped classpool. + * + * @param cl the classloader. + * @param src the original classpool. + * @return the classpool. + */ + ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src); + + /** + * Finds a scoped classpool registered under the passed in classloader. + * + * @param cl the classloader. + * @return the classpool. + */ + ClassPool findClassPool(ClassLoader cl); + + /** + * Register a classloader. + * + * @param ucl the classloader. + * @return the classpool. + */ + ClassPool registerClassLoader(ClassLoader ucl); + + /** + * Get the registered classloaders. + * + * @return the registered classloaders. + */ + Map getRegisteredCLs(); + + /** + * This method will check to see if a register classloader has been + * undeployed (as in JBoss). + */ + void clearUnregisteredClassLoaders(); + + /** + * Unregisters a classpool and unregisters its classloader. + * + * @param cl the classloader the pool is stored under. + */ + void unregisterClassLoader(ClassLoader cl); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java new file mode 100644 index 0000000..3f0e5ae --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java @@ -0,0 +1,188 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.scopedpool; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.WeakHashMap; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.LoaderClassPath; + +/** + * An implementation of ScopedClassPoolRepository. + * It is an singleton. + * + * @author Kabir Khan + * @version $Revision: 1.4 $ + */ +public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository { + /** The instance */ + private static final ScopedClassPoolRepositoryImpl instance = new ScopedClassPoolRepositoryImpl(); + + /** Whether to prune */ + private boolean prune = true; + + /** Whether to prune when added to the classpool's cache */ + boolean pruneWhenCached; + + /** The registered classloaders */ + protected Map registeredCLs = Collections + .synchronizedMap(new WeakHashMap()); + + /** The default class pool */ + protected ClassPool classpool; + + /** The factory for creating class pools */ + protected ScopedClassPoolFactory factory = new ScopedClassPoolFactoryImpl(); + + /** + * Get the instance. + * + * @return the instance. + */ + public static ScopedClassPoolRepository getInstance() { + return instance; + } + + /** + * Singleton. + */ + private ScopedClassPoolRepositoryImpl() { + classpool = ClassPool.getDefault(); + // FIXME This doesn't look correct + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + classpool.insertClassPath(new LoaderClassPath(cl)); + } + + /** + * Returns the value of the prune attribute. + * + * @return the prune. + */ + public boolean isPrune() { + return prune; + } + + /** + * Set the prune attribute. + * + * @param prune a new value. + */ + public void setPrune(boolean prune) { + this.prune = prune; + } + + /** + * Create a scoped classpool. + * + * @param cl the classloader. + * @param src the original classpool. + * @return the classpool + */ + public ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src) { + return factory.create(cl, src, this); + } + + public ClassPool findClassPool(ClassLoader cl) { + if (cl == null) + return registerClassLoader(ClassLoader.getSystemClassLoader()); + + return registerClassLoader(cl); + } + + /** + * Register a classloader. + * + * @param ucl the classloader. + * @return the classpool + */ + public ClassPool registerClassLoader(ClassLoader ucl) { + synchronized (registeredCLs) { + // FIXME: Probably want to take this method out later + // so that AOP framework can be independent of JMX + // This is in here so that we can remove a UCL from the ClassPool as + // a + // ClassPool.classpath + if (registeredCLs.containsKey(ucl)) { + return (ClassPool)registeredCLs.get(ucl); + } + ScopedClassPool pool = createScopedClassPool(ucl, classpool); + registeredCLs.put(ucl, pool); + return pool; + } + } + + /** + * Get the registered classloaders. + */ + public Map getRegisteredCLs() { + clearUnregisteredClassLoaders(); + return registeredCLs; + } + + /** + * This method will check to see if a register classloader has been + * undeployed (as in JBoss) + */ + public void clearUnregisteredClassLoaders() { + ArrayList toUnregister = null; + synchronized (registeredCLs) { + Iterator it = registeredCLs.values().iterator(); + while (it.hasNext()) { + ScopedClassPool pool = (ScopedClassPool)it.next(); + if (pool.isUnloadedClassLoader()) { + it.remove(); + ClassLoader cl = pool.getClassLoader(); + if (cl != null) { + if (toUnregister == null) { + toUnregister = new ArrayList(); + } + toUnregister.add(cl); + } + } + } + if (toUnregister != null) { + for (int i = 0; i < toUnregister.size(); i++) { + unregisterClassLoader((ClassLoader)toUnregister.get(i)); + } + } + } + } + + public void unregisterClassLoader(ClassLoader cl) { + synchronized (registeredCLs) { + ScopedClassPool pool = (ScopedClassPool)registeredCLs.remove(cl); + if (pool != null) + pool.close(); + } + } + + public void insertDelegate(ScopedClassPoolRepository delegate) { + // Noop - this is the end + } + + public void setClassPoolFactory(ScopedClassPoolFactory factory) { + this.factory = factory; + } + + public ScopedClassPoolFactory getClassPoolFactory() { + return factory; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java b/src/main/java/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java new file mode 100644 index 0000000..3743ca6 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java @@ -0,0 +1,233 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.scopedpool; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * This Map will remove entries when the value in the map has been cleaned from + * garbage collection + * + * @version $Revision: 1.4 $ + * @author Bill Burke + */ +public class SoftValueHashMap extends AbstractMap implements Map { + private static class SoftValueRef extends SoftReference { + public Object key; + + private SoftValueRef(Object key, Object val, ReferenceQueue q) { + super(val, q); + this.key = key; + } + + private static SoftValueRef create(Object key, Object val, + ReferenceQueue q) { + if (val == null) + return null; + else + return new SoftValueRef(key, val, q); + } + + } + + /** + * Returns a set of the mappings contained in this hash table. + */ + public Set entrySet() { + processQueue(); + return hash.entrySet(); + } + + /* Hash table mapping WeakKeys to values */ + private Map hash; + + /* Reference queue for cleared WeakKeys */ + private ReferenceQueue queue = new ReferenceQueue(); + + /* + * Remove all invalidated entries from the map, that is, remove all entries + * whose values have been discarded. + */ + private void processQueue() { + SoftValueRef ref; + while ((ref = (SoftValueRef)queue.poll()) != null) { + if (ref == (SoftValueRef)hash.get(ref.key)) { + // only remove if it is the *exact* same WeakValueRef + // + hash.remove(ref.key); + } + } + } + + /* -- Constructors -- */ + + /** + * Constructs a new, empty WeakHashMap with the given initial + * capacity and the given load factor. + * + * @param initialCapacity + * The initial capacity of the WeakHashMap + * + * @param loadFactor + * The load factor of the WeakHashMap + * + * @throws IllegalArgumentException + * If the initial capacity is less than zero, or if the load + * factor is nonpositive + */ + public SoftValueHashMap(int initialCapacity, float loadFactor) { + hash = new HashMap(initialCapacity, loadFactor); + } + + /** + * Constructs a new, empty WeakHashMap with the given initial + * capacity and the default load factor, which is 0.75. + * + * @param initialCapacity + * The initial capacity of the WeakHashMap + * + * @throws IllegalArgumentException + * If the initial capacity is less than zero + */ + public SoftValueHashMap(int initialCapacity) { + hash = new HashMap(initialCapacity); + } + + /** + * Constructs a new, empty WeakHashMap with the default + * initial capacity and the default load factor, which is 0.75. + */ + public SoftValueHashMap() { + hash = new HashMap(); + } + + /** + * Constructs a new WeakHashMap with the same mappings as the + * specified Map. The WeakHashMap is created with + * an initial capacity of twice the number of mappings in the specified map + * or 11 (whichever is greater), and a default load factor, which is + * 0.75. + * + * @param t the map whose mappings are to be placed in this map. + */ + public SoftValueHashMap(Map t) { + this(Math.max(2 * t.size(), 11), 0.75f); + putAll(t); + } + + /* -- Simple queries -- */ + + /** + * Returns the number of key-value mappings in this map. Note: + * In contrast with most implementations of the + * Map interface, the time required by this operation is + * linear in the size of the map. + */ + public int size() { + processQueue(); + return hash.size(); + } + + /** + * Returns true if this map contains no key-value mappings. + */ + public boolean isEmpty() { + processQueue(); + return hash.isEmpty(); + } + + /** + * Returns true if this map contains a mapping for the + * specified key. + * + * @param key + * The key whose presence in this map is to be tested. + */ + public boolean containsKey(Object key) { + processQueue(); + return hash.containsKey(key); + } + + /* -- Lookup and modification operations -- */ + + /** + * Returns the value to which this map maps the specified key. + * If this map does not contain a value for this key, then return + * null. + * + * @param key + * The key whose associated value, if any, is to be returned. + */ + public Object get(Object key) { + processQueue(); + SoftReference ref = (SoftReference)hash.get(key); + if (ref != null) + return ref.get(); + return null; + } + + /** + * Updates this map so that the given key maps to the given + * value. If the map previously contained a mapping for + * key then that mapping is replaced and the previous value + * is returned. + * + * @param key + * The key that is to be mapped to the given value + * @param value + * The value to which the given key is to be + * mapped + * + * @return The previous value to which this key was mapped, or + * null if if there was no mapping for the key + */ + public Object put(Object key, Object value) { + processQueue(); + Object rtn = hash.put(key, SoftValueRef.create(key, value, queue)); + if (rtn != null) + rtn = ((SoftReference)rtn).get(); + return rtn; + } + + /** + * Removes the mapping for the given key from this map, if + * present. + * + * @param key + * The key whose mapping is to be removed. + * + * @return The value to which this key was mapped, or null if + * there was no mapping for the key. + */ + public Object remove(Object key) { + processQueue(); + return hash.remove(key); + } + + /** + * Removes all mappings from this map. + */ + public void clear() { + processQueue(); + hash.clear(); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/package.html b/src/main/java/com/wenshuo/agent/javassist/scopedpool/package.html new file mode 100644 index 0000000..946e5e1 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/scopedpool/package.html @@ -0,0 +1,7 @@ + + +

A custom class pool for several JBoss products. +It is not part of Javassist. +

+ + diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/Callback.java b/src/main/java/com/wenshuo/agent/javassist/tools/Callback.java new file mode 100644 index 0000000..56ba4fb --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/Callback.java @@ -0,0 +1,152 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools; + +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.CtBehavior; + +import java.util.HashMap; +import java.util.UUID; + +/** + * Creates bytecode that when executed calls back to the instance's result method. + * + *

Example of how to create and insert a callback:

+ *
+ * ctMethod.insertAfter(new Callback("Thread.currentThread()") {
+ *     public void result(Object... objects) {
+ *         Thread thread = (Thread) objects[0];
+ *         // do something with thread...
+ *     }
+ * }.sourceCode());
+ * 
+ *

Contains utility methods for inserts callbacks in CtBehaviour, example:

+ *
+ * insertAfter(ctBehaviour, new Callback("Thread.currentThread(), dummyString") {
+ *     public void result(Object... objects) {
+ *         Thread thread = (Thread) objects[0];
+ *         // do something with thread...
+ *     }
+ * });
+ * 
+ * + * @author Marten Hedborg + */ +public abstract class Callback { + + public static HashMap callbacks = new HashMap(); + + private final String sourceCode; + + /** + * Constructs a new Callback object. + * + * @param src The source code representing the inserted callback bytecode. + * Can be one or many single statements each returning one object. + * If many single statements are used they must be comma separated. + */ + public Callback(String src){ + String uuid = UUID.randomUUID().toString(); + callbacks.put(uuid, this); + sourceCode = "((javassist.tools.Callback) javassist.tools.Callback.callbacks.get(\""+uuid+"\")).result(new Object[]{"+src+"});"; + } + + /** + * Gets called when bytecode is executed + * + * @param objects Objects that the bytecode in callback returns + */ + public abstract void result(Object... objects); + + @Override + public String toString(){ + return sourceCode(); + } + + public String sourceCode(){ + return sourceCode; + } + + /** + * Utility method to insert callback at the beginning of the body. + * + * @param callback The callback + * + * @see CtBehavior#insertBefore(String) + */ + public static void insertBefore(CtBehavior behavior, Callback callback) + throws CannotCompileException + { + behavior.insertBefore(callback.toString()); + } + + /** + * Utility method to inserts callback at the end of the body. + * The callback is inserted just before every return instruction. + * It is not executed when an exception is thrown. + * + * @param behavior The behaviour to insert callback in + * @param callback The callback + * + * @see CtBehavior#insertAfter(String, boolean) + */ + public static void insertAfter(CtBehavior behavior,Callback callback) + throws CannotCompileException + { + behavior.insertAfter(callback.toString(), false); + } + + /** + * Utility method to inserts callback at the end of the body. + * The callback is inserted just before every return instruction. + * It is not executed when an exception is thrown. + * + * @param behavior The behaviour to insert callback in + * @param callback The callback representing the inserted. + * @param asFinally True if the inserted is executed + * Not only when the control normally returns + * but also when an exception is thrown. + * If this parameter is true, the inserted code cannot + * access local variables. + * + * @see CtBehavior#insertAfter(String, boolean) + */ + public static void insertAfter(CtBehavior behavior, Callback callback, boolean asFinally) + throws CannotCompileException + { + behavior.insertAfter(callback.toString(), asFinally); + } + + /** + * Utility method to inserts callback at the specified line in the body. + * + * @param behavior The behaviour to insert callback in + * @param callback The callback representing. + * @param lineNum The line number. The callback is inserted at the + * beginning of the code at the line specified by this + * line number. + * + * @return The line number at which the callback has been inserted. + * + * @see CtBehavior#insertAt(int, String) + */ + public static int insertAt(CtBehavior behavior, Callback callback, int lineNum) + throws CannotCompileException + { + return behavior.insertAt(lineNum, callback.toString()); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/Dump.java b/src/main/java/com/wenshuo/agent/javassist/tools/Dump.java new file mode 100644 index 0000000..8a504ee --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/Dump.java @@ -0,0 +1,58 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools; + +import java.io.*; +import com.wenshuo.agent.javassist.bytecode.ClassFile; +import com.wenshuo.agent.javassist.bytecode.ClassFilePrinter; + +/** + * Dump is a tool for viewing the class definition in the given + * class file. Unlike the JDK javap tool, Dump works even if + * the class file is broken. + * + *

For example, + *

% java javassist.tools.Dump foo.class
+ * + *

prints the contents of the constant pool and the list of methods + * and fields. + */ +public class Dump { + private Dump() {} + + /** + * Main method. + * + * @param args args[0] is the class file name. + */ + public static void main(String[] args) throws Exception { + if (args.length != 1) { + System.err.println("Usage: java Dump "); + return; + } + + DataInputStream in = new DataInputStream( + new FileInputStream(args[0])); + ClassFile w = new ClassFile(in); + PrintWriter out = new PrintWriter(System.out, true); + out.println("*** constant pool ***"); + w.getConstPool().print(out); + out.println(); + out.println("*** members ***"); + ClassFilePrinter.print(w, out); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/framedump.java b/src/main/java/com/wenshuo/agent/javassist/tools/framedump.java new file mode 100644 index 0000000..623ffc3 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/framedump.java @@ -0,0 +1,48 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.tools; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.bytecode.analysis.FramePrinter; + +/** + * framedump is a tool for viewing a merged combination of the instructions and frame state + * of all methods in a class. + * + *

For example, + *

% java javassist.tools.framedump foo.class
+ */ +public class framedump { + private framedump() {} + + /** + * Main method. + * + * @param args args[0] is the class file name. + */ + public static void main(String[] args) throws Exception { + if (args.length != 1) { + System.err.println("Usage: java javassist.tools.framedump "); + return; + } + + ClassPool pool = ClassPool.getDefault(); + CtClass clazz = pool.get(args[0]); + System.out.println("Frame Dump of " + clazz.getName() + ":"); + FramePrinter.print(clazz, System.out); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/package.html b/src/main/java/com/wenshuo/agent/javassist/tools/package.html new file mode 100644 index 0000000..bee6208 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/package.html @@ -0,0 +1,6 @@ + + +Covenient tools. + + + diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java new file mode 100644 index 0000000..de3c782 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java @@ -0,0 +1,30 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +/** + * Signals that ClassMetaobject.newInstance() fails. + */ +public class CannotCreateException extends Exception { + public CannotCreateException(String s) { + super(s); + } + + public CannotCreateException(Exception e) { + super("by " + e.toString()); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java new file mode 100644 index 0000000..7907022 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java @@ -0,0 +1,69 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +import java.lang.reflect.InvocationTargetException; +import java.lang.IllegalAccessException; + +/** + * Thrown when method invocation using the reflection API has thrown + * an exception. + * + * @see javassist.tools.reflect.Metaobject#trapMethodcall(int, Object[]) + * @see javassist.tools.reflect.ClassMetaobject#trapMethodcall(int, Object[]) + * @see javassist.tools.reflect.ClassMetaobject#invoke(Object, int, Object[]) + */ +public class CannotInvokeException extends RuntimeException { + + private Throwable err = null; + + /** + * Returns the cause of this exception. It may return null. + */ + public Throwable getReason() { return err; } + + /** + * Constructs a CannotInvokeException with an error message. + */ + public CannotInvokeException(String reason) { + super(reason); + } + + /** + * Constructs a CannotInvokeException with an InvocationTargetException. + */ + public CannotInvokeException(InvocationTargetException e) { + super("by " + e.getTargetException().toString()); + err = e.getTargetException(); + } + + /** + * Constructs a CannotInvokeException with an IllegalAccessException. + */ + public CannotInvokeException(IllegalAccessException e) { + super("by " + e.toString()); + err = e; + } + + /** + * Constructs a CannotInvokeException with an ClassNotFoundException. + */ + public CannotInvokeException(ClassNotFoundException e) { + super("by " + e.toString()); + err = e; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java new file mode 100644 index 0000000..f2673c9 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java @@ -0,0 +1,35 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +import com.wenshuo.agent.javassist.CannotCompileException; + +/** + * Thrown by makeReflective() in Reflection + * when there is an attempt to reflect + * a class that is either an interface or a subclass of + * either ClassMetaobject or Metaobject. + * + * @author Brett Randall + * @see javassist.tools.reflect.Reflection#makeReflective(CtClass,CtClass,CtClass) + * @see javassist.CannotCompileException + */ +public class CannotReflectException extends CannotCompileException { + public CannotReflectException(String msg) { + super(msg); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java new file mode 100644 index 0000000..78fb42f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java @@ -0,0 +1,370 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +import java.lang.reflect.*; +import java.util.Arrays; +import java.io.Serializable; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * A runtime class metaobject. + * + *

A ClassMetaobject is created for every + * class of reflective objects. It can be used to hold values + * shared among the reflective objects of the same class. + * + *

To obtain a class metaobject, calls _getClass() + * on a reflective object. For example, + * + *

ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass();
+ * 
+ * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.Metalevel + */ +public class ClassMetaobject implements Serializable { + /** + * The base-level methods controlled by a metaobject + * are renamed so that they begin with + * methodPrefix "_m_". + */ + static final String methodPrefix = "_m_"; + static final int methodPrefixLen = 3; + + private Class javaClass; + private Constructor[] constructors; + private Method[] methods; + + /** + * Specifies how a java.lang.Class object is loaded. + * + *

If true, it is loaded by: + *

Thread.currentThread().getContextClassLoader().loadClass()
+ *

If false, it is loaded by Class.forName(). + * The default value is false. + */ + public static boolean useContextClassLoader = false; + + /** + * Constructs a ClassMetaobject. + * + * @param params params[0] is the name of the class + * of the reflective objects. + */ + public ClassMetaobject(String[] params) + { + try { + javaClass = getClassObject(params[0]); + } + catch (ClassNotFoundException e) { + throw new RuntimeException("not found: " + params[0] + + ", useContextClassLoader: " + + Boolean.toString(useContextClassLoader), e); + } + + constructors = javaClass.getConstructors(); + methods = null; + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.writeUTF(javaClass.getName()); + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + javaClass = getClassObject(in.readUTF()); + constructors = javaClass.getConstructors(); + methods = null; + } + + private Class getClassObject(String name) throws ClassNotFoundException { + if (useContextClassLoader) + return Thread.currentThread().getContextClassLoader() + .loadClass(name); + else + return Class.forName(name); + } + + /** + * Obtains the java.lang.Class representing this class. + */ + public final Class getJavaClass() { + return javaClass; + } + + /** + * Obtains the name of this class. + */ + public final String getName() { + return javaClass.getName(); + } + + /** + * Returns true if obj is an instance of this class. + */ + public final boolean isInstance(Object obj) { + return javaClass.isInstance(obj); + } + + /** + * Creates a new instance of the class. + * + * @param args the arguments passed to the constructor. + */ + public final Object newInstance(Object[] args) + throws CannotCreateException + { + int n = constructors.length; + for (int i = 0; i < n; ++i) { + try { + return constructors[i].newInstance(args); + } + catch (IllegalArgumentException e) { + // try again + } + catch (InstantiationException e) { + throw new CannotCreateException(e); + } + catch (IllegalAccessException e) { + throw new CannotCreateException(e); + } + catch (InvocationTargetException e) { + throw new CannotCreateException(e); + } + } + + throw new CannotCreateException("no constructor matches"); + } + + /** + * Is invoked when static fields of the base-level + * class are read and the runtime system intercepts it. + * This method simply returns the value of the field. + * + *

Every subclass of this class should redefine this method. + */ + public Object trapFieldRead(String name) { + Class jc = getJavaClass(); + try { + return jc.getField(name).get(null); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e.toString()); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Is invoked when static fields of the base-level + * class are modified and the runtime system intercepts it. + * This method simply sets the field to the given value. + * + *

Every subclass of this class should redefine this method. + */ + public void trapFieldWrite(String name, Object value) { + Class jc = getJavaClass(); + try { + jc.getField(name).set(null, value); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e.toString()); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Invokes a method whose name begins with + * methodPrefix "_m_" and the identifier. + * + * @exception CannotInvokeException if the invocation fails. + */ + static public Object invoke(Object target, int identifier, Object[] args) + throws Throwable + { + Method[] allmethods = target.getClass().getMethods(); + int n = allmethods.length; + String head = methodPrefix + identifier; + for (int i = 0; i < n; ++i) + if (allmethods[i].getName().startsWith(head)) { + try { + return allmethods[i].invoke(target, args); + } catch (java.lang.reflect.InvocationTargetException e) { + throw e.getTargetException(); + } catch (java.lang.IllegalAccessException e) { + throw new CannotInvokeException(e); + } + } + + throw new CannotInvokeException("cannot find a method"); + } + + /** + * Is invoked when static methods of the base-level + * class are called and the runtime system intercepts it. + * This method simply executes the intercepted method invocation + * with the original parameters and returns the resulting value. + * + *

Every subclass of this class should redefine this method. + */ + public Object trapMethodcall(int identifier, Object[] args) + throws Throwable + { + try { + Method[] m = getReflectiveMethods(); + return m[identifier].invoke(null, args); + } + catch (java.lang.reflect.InvocationTargetException e) { + throw e.getTargetException(); + } + catch (java.lang.IllegalAccessException e) { + throw new CannotInvokeException(e); + } + } + + /** + * Returns an array of the methods defined on the given reflective + * object. This method is for the internal use only. + */ + public final Method[] getReflectiveMethods() { + if (methods != null) + return methods; + + Class baseclass = getJavaClass(); + Method[] allmethods = baseclass.getDeclaredMethods(); + int n = allmethods.length; + int[] index = new int[n]; + int max = 0; + for (int i = 0; i < n; ++i) { + Method m = allmethods[i]; + String mname = m.getName(); + if (mname.startsWith(methodPrefix)) { + int k = 0; + for (int j = methodPrefixLen;; ++j) { + char c = mname.charAt(j); + if ('0' <= c && c <= '9') + k = k * 10 + c - '0'; + else + break; + } + + index[i] = ++k; + if (k > max) + max = k; + } + } + + methods = new Method[max]; + for (int i = 0; i < n; ++i) + if (index[i] > 0) + methods[index[i] - 1] = allmethods[i]; + + return methods; + } + + /** + * Returns the java.lang.reflect.Method object representing + * the method specified by identifier. + * + *

Note that the actual method returned will be have an altered, + * reflective name i.e. _m_2_... + * + * @param identifier the identifier index + * given to trapMethodcall() etc. + * @see Metaobject#trapMethodcall(int,Object[]) + * @see #trapMethodcall(int,Object[]) + */ + public final Method getMethod(int identifier) { + return getReflectiveMethods()[identifier]; + } + + /** + * Returns the name of the method specified + * by identifier. + */ + public final String getMethodName(int identifier) { + String mname = getReflectiveMethods()[identifier].getName(); + int j = ClassMetaobject.methodPrefixLen; + for (;;) { + char c = mname.charAt(j++); + if (c < '0' || '9' < c) + break; + } + + return mname.substring(j); + } + + /** + * Returns an array of Class objects representing the + * formal parameter types of the method specified + * by identifier. + */ + public final Class[] getParameterTypes(int identifier) { + return getReflectiveMethods()[identifier].getParameterTypes(); + } + + /** + * Returns a Class objects representing the + * return type of the method specified by identifier. + */ + public final Class getReturnType(int identifier) { + return getReflectiveMethods()[identifier].getReturnType(); + } + + /** + * Returns the identifier index of the method, as identified by its + * original name. + * + *

This method is useful, in conjuction with + * {@link ClassMetaobject#getMethod(int)}, to obtain a quick reference + * to the original method in the reflected class (i.e. not the proxy + * method), using the original name of the method. + * + *

Written by Brett Randall and Shigeru Chiba. + * + * @param originalName The original name of the reflected method + * @param argTypes array of Class specifying the method signature + * @return the identifier index of the original method + * @throws NoSuchMethodException if the method does not exist + * + * @see ClassMetaobject#getMethod(int) + */ + public final int getMethodIndex(String originalName, Class[] argTypes) + throws NoSuchMethodException + { + Method[] mthds = getReflectiveMethods(); + for (int i = 0; i < mthds.length; i++) { + if (mthds[i] == null) + continue; + + // check name and parameter types match + if (getMethodName(i).equals(originalName) + && Arrays.equals(argTypes, mthds[i].getParameterTypes())) + return i; + } + + throw new NoSuchMethodException("Method " + originalName + + " not found"); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Compiler.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Compiler.java new file mode 100644 index 0000000..0953163 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Compiler.java @@ -0,0 +1,163 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.ClassPool; +import java.io.PrintStream; + +class CompiledClass { + public String classname; + public String metaobject; + public String classobject; +} + +/** + * A bytecode translator for reflection. + * + *

This translator directly modifies class files on a local disk so that + * the classes represented by those class files are reflective. + * After the modification, the class files can be run with the standard JVM + * without javassist.tools.reflect.Loader + * or any other user-defined class loader. + * + *

The modified class files are given as the command-line parameters, + * which are a sequence of fully-qualified class names followed by options: + * + *

-m classname : specifies the class of the + * metaobjects associated with instances of the class followed by + * this option. The default is javassit.reflect.Metaobject. + * + *

-c classname : specifies the class of the + * class metaobjects associated with instances of the class followed by + * this option. The default is javassit.reflect.ClassMetaobject. + * + *

If a class name is not followed by any options, the class indicated + * by that class name is not reflective. + * + *

For example, + *

% java Compiler Dog -m MetaDog -c CMetaDog Cat -m MetaCat Cow
+ * 
+ * + *

modifies class files Dog.class, Cat.class, + * and Cow.class. + * The metaobject of a Dog object is a MetaDog object and the class + * metaobject is a CMetaDog object. + * The metaobject of a Cat object is a MetaCat object but + * the class metaobject is a default one. + * Cow objects are not reflective. + * + *

Note that if the super class is also made reflective, it must be done + * before the sub class. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + * @see javassist.tools.reflect.Reflection + */ +public class Compiler { + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + help(System.err); + return; + } + + CompiledClass[] entries = new CompiledClass[args.length]; + int n = parse(args, entries); + + if (n < 1) { + System.err.println("bad parameter."); + return; + } + + processClasses(entries, n); + } + + private static void processClasses(CompiledClass[] entries, int n) + throws Exception + { + Reflection implementor = new Reflection(); + ClassPool pool = ClassPool.getDefault(); + implementor.start(pool); + + for (int i = 0; i < n; ++i) { + CtClass c = pool.get(entries[i].classname); + if (entries[i].metaobject != null + || entries[i].classobject != null) { + String metaobj, classobj; + + if (entries[i].metaobject == null) + metaobj = "javassist.tools.reflect.Metaobject"; + else + metaobj = entries[i].metaobject; + + if (entries[i].classobject == null) + classobj = "javassist.tools.reflect.ClassMetaobject"; + else + classobj = entries[i].classobject; + + if (!implementor.makeReflective(c, pool.get(metaobj), + pool.get(classobj))) + System.err.println("Warning: " + c.getName() + + " is reflective. It was not changed."); + + System.err.println(c.getName() + ": " + metaobj + ", " + + classobj); + } + else + System.err.println(c.getName() + ": not reflective"); + } + + for (int i = 0; i < n; ++i) { + implementor.onLoad(pool, entries[i].classname); + pool.get(entries[i].classname).writeFile(); + } + } + + private static int parse(String[] args, CompiledClass[] result) { + int n = -1; + for (int i = 0; i < args.length; ++i) { + String a = args[i]; + if (a.equals("-m")) + if (n < 0 || i + 1 > args.length) + return -1; + else + result[n].metaobject = args[++i]; + else if (a.equals("-c")) + if (n < 0 || i + 1 > args.length) + return -1; + else + result[n].classobject = args[++i]; + else if (a.charAt(0) == '-') + return -1; + else { + CompiledClass cc = new CompiledClass(); + cc.classname = a; + cc.metaobject = null; + cc.classobject = null; + result[++n] = cc; + } + } + + return n + 1; + } + + private static void help(PrintStream out) { + out.println("Usage: java javassist.tools.reflect.Compiler"); + out.println(" ( [-m ] [-c ])+"); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Loader.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Loader.java new file mode 100644 index 0000000..4b9d1e3 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Loader.java @@ -0,0 +1,162 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.ClassPool; + +/** + * A class loader for reflection. + * + *

To run a program, say MyApp, + * including a reflective class, + * you must write a start-up program as follows: + * + *

+ * public class Main {
+ *   public static void main(String[] args) throws Throwable {
+ *     javassist.tools.reflect.Loader cl
+ *         = (javassist.tools.reflect.Loader)Main.class.getClassLoader();
+ *     cl.makeReflective("Person", "MyMetaobject",
+ *                       "javassist.tools.reflect.ClassMetaobject");
+ *     cl.run("MyApp", args);
+ *   }
+ * }
+ * 
+ * + *

Then run this program as follows: + * + *

% java javassist.tools.reflect.Loader Main arg1, ...
+ * + *

This command runs Main.main() with arg1, ... + * and Main.main() runs MyApp.main() with + * arg1, ... + * The Person class is modified + * to be a reflective class. Method calls on a Person + * object are intercepted by an instance of MyMetaobject. + * + *

Also, you can run MyApp in a slightly different way: + * + *

+ * public class Main2 {
+ *   public static void main(String[] args) throws Throwable {
+ *     javassist.tools.reflect.Loader cl = new javassist.tools.reflect.Loader();
+ *     cl.makeReflective("Person", "MyMetaobject",
+ *                       "javassist.tools.reflect.ClassMetaobject");
+ *     cl.run("MyApp", args);
+ *   }
+ * }
+ * 
+ * + *

This program is run as follows: + * + *

% java Main2 arg1, ...
+ * + *

The difference from the former one is that the class Main + * is loaded by javassist.tools.reflect.Loader whereas the class + * Main2 is not. Thus, Main belongs + * to the same name space (security domain) as MyApp + * whereas Main2 does not; Main2 belongs + * to the same name space as javassist.tools.reflect.Loader. + * For more details, + * see the notes in the manual page of javassist.Loader. + * + *

The class Main2 is equivalent to this class: + * + *

+ * public class Main3 {
+ *   public static void main(String[] args) throws Throwable {
+ *     Reflection reflection = new Reflection();
+ *     javassist.Loader cl
+ *         = new javassist.Loader(ClassPool.getDefault(reflection));
+ *     reflection.makeReflective("Person", "MyMetaobject",
+ *                               "javassist.tools.reflect.ClassMetaobject");
+ *     cl.run("MyApp", args);
+ *   }
+ * }
+ * 
+ * + *

Note: + * + *

javassist.tools.reflect.Loader does not make a class reflective + * if that class is in a java.* or + * javax.* pacakge because of the specifications + * on the class loading algorithm of Java. The JVM does not allow to + * load such a system class with a user class loader. + * + *

To avoid this limitation, those classes should be statically + * modified with javassist.tools.reflect.Compiler and the original + * class files should be replaced. + * + * @see javassist.tools.reflect.Reflection + * @see javassist.tools.reflect.Compiler + * @see javassist.Loader + */ +public class Loader extends com.wenshuo.agent.javassist.Loader { + protected Reflection reflection; + + /** + * Loads a class with an instance of Loader + * and calls main() in that class. + * + * @param args command line parameters. + *
  args[0] is the class name to be loaded. + *
  args[1..n] are parameters passed + * to the target main(). + */ + public static void main(String[] args) throws Throwable { + Loader cl = new Loader(); + cl.run(args); + } + + /** + * Constructs a new class loader. + */ + public Loader() throws CannotCompileException, NotFoundException { + super(); + delegateLoadingOf("javassist.tools.reflect.Loader"); + + reflection = new Reflection(); + ClassPool pool = ClassPool.getDefault(); + addTranslator(pool, reflection); + } + + /** + * Produces a reflective class. + * If the super class is also made reflective, it must be done + * before the sub class. + * + * @param clazz the reflective class. + * @param metaobject the class of metaobjects. + * It must be a subclass of + * Metaobject. + * @param metaclass the class of the class metaobject. + * It must be a subclass of + * ClassMetaobject. + * @return false if the class is already reflective. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + */ + public boolean makeReflective(String clazz, + String metaobject, String metaclass) + throws CannotCompileException, NotFoundException + { + return reflection.makeReflective(clazz, metaobject, metaclass); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java new file mode 100644 index 0000000..d8c1f4a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java @@ -0,0 +1,39 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +/** + * An interface to access a metaobject and a class metaobject. + * This interface is implicitly implemented by the reflective + * class. + */ +public interface Metalevel { + /** + * Obtains the class metaobject associated with this object. + */ + ClassMetaobject _getClass(); + + /** + * Obtains the metaobject associated with this object. + */ + Metaobject _getMetaobject(); + + /** + * Changes the metaobject associated with this object. + */ + void _setMetaobject(Metaobject m); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java new file mode 100644 index 0000000..f96444e --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java @@ -0,0 +1,239 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +import java.lang.reflect.Method; +import java.io.Serializable; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * A runtime metaobject. + * + *

A Metaobject is created for + * every object at the base level. A different reflective object is + * associated with a different metaobject. + * + *

The metaobject intercepts method calls + * on the reflective object at the base-level. To change the behavior + * of the method calls, a subclass of Metaobject + * should be defined. + * + *

To obtain a metaobject, calls _getMetaobject() + * on a reflective object. For example, + * + *

+ * Metaobject m = ((Metalevel)reflectiveObject)._getMetaobject();
+ * 
+ * + * @see javassist.tools.reflect.ClassMetaobject + * @see javassist.tools.reflect.Metalevel + */ +public class Metaobject implements Serializable { + protected ClassMetaobject classmetaobject; + protected Metalevel baseobject; + protected Method[] methods; + + /** + * Constructs a Metaobject. The metaobject is + * constructed before the constructor is called on the base-level + * object. + * + * @param self the object that this metaobject is associated with. + * @param args the parameters passed to the constructor of + * self. + */ + public Metaobject(Object self, Object[] args) { + baseobject = (Metalevel)self; + classmetaobject = baseobject._getClass(); + methods = classmetaobject.getReflectiveMethods(); + } + + /** + * Constructs a Metaobject without initialization. + * If calling this constructor, a subclass should be responsible + * for initialization. + */ + protected Metaobject() { + baseobject = null; + classmetaobject = null; + methods = null; + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.writeObject(baseobject); + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + baseobject = (Metalevel)in.readObject(); + classmetaobject = baseobject._getClass(); + methods = classmetaobject.getReflectiveMethods(); + } + + /** + * Obtains the class metaobject associated with this metaobject. + * + * @see javassist.tools.reflect.ClassMetaobject + */ + public final ClassMetaobject getClassMetaobject() { + return classmetaobject; + } + + /** + * Obtains the object controlled by this metaobject. + */ + public final Object getObject() { + return baseobject; + } + + /** + * Changes the object controlled by this metaobject. + * + * @param self the object + */ + public final void setObject(Object self) { + baseobject = (Metalevel)self; + classmetaobject = baseobject._getClass(); + methods = classmetaobject.getReflectiveMethods(); + + // call _setMetaobject() after the metaobject is settled. + baseobject._setMetaobject(this); + } + + /** + * Returns the name of the method specified + * by identifier. + */ + public final String getMethodName(int identifier) { + String mname = methods[identifier].getName(); + int j = ClassMetaobject.methodPrefixLen; + for (;;) { + char c = mname.charAt(j++); + if (c < '0' || '9' < c) + break; + } + + return mname.substring(j); + } + + /** + * Returns an array of Class objects representing the + * formal parameter types of the method specified + * by identifier. + */ + public final Class[] getParameterTypes(int identifier) { + return methods[identifier].getParameterTypes(); + } + + /** + * Returns a Class objects representing the + * return type of the method specified by identifier. + */ + public final Class getReturnType(int identifier) { + return methods[identifier].getReturnType(); + } + + /** + * Is invoked when public fields of the base-level + * class are read and the runtime system intercepts it. + * This method simply returns the value of the field. + * + *

Every subclass of this class should redefine this method. + */ + public Object trapFieldRead(String name) { + Class jc = getClassMetaobject().getJavaClass(); + try { + return jc.getField(name).get(getObject()); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e.toString()); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Is invoked when public fields of the base-level + * class are modified and the runtime system intercepts it. + * This method simply sets the field to the given value. + * + *

Every subclass of this class should redefine this method. + */ + public void trapFieldWrite(String name, Object value) { + Class jc = getClassMetaobject().getJavaClass(); + try { + jc.getField(name).set(getObject(), value); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e.toString()); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Is invoked when base-level method invocation is intercepted. + * This method simply executes the intercepted method invocation + * with the original parameters and returns the resulting value. + * + *

Every subclass of this class should redefine this method. + * + *

Note: this method is not invoked if the base-level method + * is invoked by a constructor in the super class. For example, + * + *

+     * abstract class A {
+     *   abstract void initialize();
+     *   A() {
+     *       initialize();    // not intercepted
+     *   }
+     * }
+     *
+     * class B extends A {
+     *   void initialize() { System.out.println("initialize()"); }
+     *   B() {
+     *       super();
+     *       initialize();    // intercepted
+     *   }
+     * }
+ * + *

if an instance of B is created, + * the invocation of initialize() in B is intercepted only once. + * The first invocation by the constructor in A is not intercepted. + * This is because the link between a base-level object and a + * metaobject is not created until the execution of a + * constructor of the super class finishes. + */ + public Object trapMethodcall(int identifier, Object[] args) + throws Throwable + { + try { + return methods[identifier].invoke(getObject(), args); + } + catch (java.lang.reflect.InvocationTargetException e) { + throw e.getTargetException(); + } + catch (java.lang.IllegalAccessException e) { + throw new CannotInvokeException(e); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Reflection.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Reflection.java new file mode 100644 index 0000000..4e2219f --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Reflection.java @@ -0,0 +1,403 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +import java.util.Iterator; +import com.wenshuo.agent.javassist.*; +import com.wenshuo.agent.javassist.CtMethod.ConstParameter; +import com.wenshuo.agent.javassist.bytecode.ClassFile; +import com.wenshuo.agent.javassist.bytecode.BadBytecode; +import com.wenshuo.agent.javassist.bytecode.MethodInfo; + +/** + * The class implementing the behavioral reflection mechanism. + * + *

If a class is reflective, + * then all the method invocations on every + * instance of that class are intercepted by the runtime + * metaobject controlling that instance. The methods inherited from the + * super classes are also intercepted except final methods. To intercept + * a final method in a super class, that super class must be also reflective. + * + *

To do this, the original class file representing a reflective class: + * + *

+ * class Person {
+ *   public int f(int i) { return i + 1; }
+ *   public int value;
+ * }
+ * 
+ * + *

is modified so that it represents a class: + * + *

+ * class Person implements Metalevel {
+ *   public int _original_f(int i) { return i + 1; }
+ *   public int f(int i) { delegate to the metaobject }
+ *
+ *   public int value;
+ *   public int _r_value() { read "value" }
+ *   public void _w_value(int v) { write "value" }
+ *
+ *   public ClassMetaobject _getClass() { return a class metaobject }
+ *   public Metaobject _getMetaobject() { return a metaobject }
+ *   public void _setMetaobject(Metaobject m) { change a metaobject }
+ * }
+ * 
+ * + * @see javassist.tools.reflect.ClassMetaobject + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.Loader + * @see javassist.tools.reflect.Compiler + */ +public class Reflection implements Translator { + + static final String classobjectField = "_classobject"; + static final String classobjectAccessor = "_getClass"; + static final String metaobjectField = "_metaobject"; + static final String metaobjectGetter = "_getMetaobject"; + static final String metaobjectSetter = "_setMetaobject"; + static final String readPrefix = "_r_"; + static final String writePrefix = "_w_"; + + static final String metaobjectClassName = "javassist.tools.reflect.Metaobject"; + static final String classMetaobjectClassName + = "javassist.tools.reflect.ClassMetaobject"; + + protected CtMethod trapMethod, trapStaticMethod; + protected CtMethod trapRead, trapWrite; + protected CtClass[] readParam; + + protected ClassPool classPool; + protected CodeConverter converter; + + private boolean isExcluded(String name) { + return name.startsWith(ClassMetaobject.methodPrefix) + || name.equals(classobjectAccessor) + || name.equals(metaobjectSetter) + || name.equals(metaobjectGetter) + || name.startsWith(readPrefix) + || name.startsWith(writePrefix); + } + + /** + * Constructs a new Reflection object. + */ + public Reflection() { + classPool = null; + converter = new CodeConverter(); + } + + /** + * Initializes the object. + */ + public void start(ClassPool pool) throws NotFoundException { + classPool = pool; + final String msg + = "javassist.tools.reflect.Sample is not found or broken."; + try { + CtClass c = classPool.get("javassist.tools.reflect.Sample"); + rebuildClassFile(c.getClassFile()); + trapMethod = c.getDeclaredMethod("trap"); + trapStaticMethod = c.getDeclaredMethod("trapStatic"); + trapRead = c.getDeclaredMethod("trapRead"); + trapWrite = c.getDeclaredMethod("trapWrite"); + readParam + = new CtClass[] { classPool.get("java.lang.Object") }; + } + catch (NotFoundException e) { + throw new RuntimeException(msg); + } catch (BadBytecode e) { + throw new RuntimeException(msg); + } + } + + /** + * Inserts hooks for intercepting accesses to the fields declared + * in reflective classes. + */ + public void onLoad(ClassPool pool, String classname) + throws CannotCompileException, NotFoundException + { + CtClass clazz = pool.get(classname); + clazz.instrument(converter); + } + + /** + * Produces a reflective class. + * If the super class is also made reflective, it must be done + * before the sub class. + * + * @param classname the name of the reflective class + * @param metaobject the class name of metaobjects. + * @param metaclass the class name of the class metaobject. + * @return false if the class is already reflective. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + */ + public boolean makeReflective(String classname, + String metaobject, String metaclass) + throws CannotCompileException, NotFoundException + { + return makeReflective(classPool.get(classname), + classPool.get(metaobject), + classPool.get(metaclass)); + } + + /** + * Produces a reflective class. + * If the super class is also made reflective, it must be done + * before the sub class. + * + * @param clazz the reflective class. + * @param metaobject the class of metaobjects. + * It must be a subclass of + * Metaobject. + * @param metaclass the class of the class metaobject. + * It must be a subclass of + * ClassMetaobject. + * @return false if the class is already reflective. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + */ + public boolean makeReflective(Class clazz, + Class metaobject, Class metaclass) + throws CannotCompileException, NotFoundException + { + return makeReflective(clazz.getName(), metaobject.getName(), + metaclass.getName()); + } + + /** + * Produces a reflective class. It modifies the given + * CtClass object and makes it reflective. + * If the super class is also made reflective, it must be done + * before the sub class. + * + * @param clazz the reflective class. + * @param metaobject the class of metaobjects. + * It must be a subclass of + * Metaobject. + * @param metaclass the class of the class metaobject. + * It must be a subclass of + * ClassMetaobject. + * @return false if the class is already reflective. + * + * @see javassist.tools.reflect.Metaobject + * @see javassist.tools.reflect.ClassMetaobject + */ + public boolean makeReflective(CtClass clazz, + CtClass metaobject, CtClass metaclass) + throws CannotCompileException, CannotReflectException, + NotFoundException + { + if (clazz.isInterface()) + throw new CannotReflectException( + "Cannot reflect an interface: " + clazz.getName()); + + if (clazz.subclassOf(classPool.get(classMetaobjectClassName))) + throw new CannotReflectException( + "Cannot reflect a subclass of ClassMetaobject: " + + clazz.getName()); + + if (clazz.subclassOf(classPool.get(metaobjectClassName))) + throw new CannotReflectException( + "Cannot reflect a subclass of Metaobject: " + + clazz.getName()); + + registerReflectiveClass(clazz); + return modifyClassfile(clazz, metaobject, metaclass); + } + + /** + * Registers a reflective class. The field accesses to the instances + * of this class are instrumented. + */ + private void registerReflectiveClass(CtClass clazz) { + CtField[] fs = clazz.getDeclaredFields(); + for (int i = 0; i < fs.length; ++i) { + CtField f = fs[i]; + int mod = f.getModifiers(); + if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { + String name = f.getName(); + converter.replaceFieldRead(f, clazz, readPrefix + name); + converter.replaceFieldWrite(f, clazz, writePrefix + name); + } + } + } + + private boolean modifyClassfile(CtClass clazz, CtClass metaobject, + CtClass metaclass) + throws CannotCompileException, NotFoundException + { + if (clazz.getAttribute("Reflective") != null) + return false; // this is already reflective. + else + clazz.setAttribute("Reflective", new byte[0]); + + CtClass mlevel = classPool.get("javassist.tools.reflect.Metalevel"); + boolean addMeta = !clazz.subtypeOf(mlevel); + if (addMeta) + clazz.addInterface(mlevel); + + processMethods(clazz, addMeta); + processFields(clazz); + + CtField f; + if (addMeta) { + f = new CtField(classPool.get("javassist.tools.reflect.Metaobject"), + metaobjectField, clazz); + f.setModifiers(Modifier.PROTECTED); + clazz.addField(f, CtField.Initializer.byNewWithParams(metaobject)); + + clazz.addMethod(CtNewMethod.getter(metaobjectGetter, f)); + clazz.addMethod(CtNewMethod.setter(metaobjectSetter, f)); + } + + f = new CtField(classPool.get("javassist.tools.reflect.ClassMetaobject"), + classobjectField, clazz); + f.setModifiers(Modifier.PRIVATE | Modifier.STATIC); + clazz.addField(f, CtField.Initializer.byNew(metaclass, + new String[] { clazz.getName() })); + + clazz.addMethod(CtNewMethod.getter(classobjectAccessor, f)); + return true; + } + + private void processMethods(CtClass clazz, boolean dontSearch) + throws CannotCompileException, NotFoundException + { + CtMethod[] ms = clazz.getMethods(); + for (int i = 0; i < ms.length; ++i) { + CtMethod m = ms[i]; + int mod = m.getModifiers(); + if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod)) + processMethods0(mod, clazz, m, i, dontSearch); + } + } + + private void processMethods0(int mod, CtClass clazz, + CtMethod m, int identifier, boolean dontSearch) + throws CannotCompileException, NotFoundException + { + CtMethod body; + String name = m.getName(); + + if (isExcluded(name)) // internally-used method inherited + return; // from a reflective class. + + CtMethod m2; + if (m.getDeclaringClass() == clazz) { + if (Modifier.isNative(mod)) + return; + + m2 = m; + if (Modifier.isFinal(mod)) { + mod &= ~Modifier.FINAL; + m2.setModifiers(mod); + } + } + else { + if (Modifier.isFinal(mod)) + return; + + mod &= ~Modifier.NATIVE; + m2 = CtNewMethod.delegator(findOriginal(m, dontSearch), clazz); + m2.setModifiers(mod); + clazz.addMethod(m2); + } + + m2.setName(ClassMetaobject.methodPrefix + identifier + + "_" + name); + + if (Modifier.isStatic(mod)) + body = trapStaticMethod; + else + body = trapMethod; + + CtMethod wmethod + = CtNewMethod.wrapped(m.getReturnType(), name, + m.getParameterTypes(), m.getExceptionTypes(), + body, ConstParameter.integer(identifier), + clazz); + wmethod.setModifiers(mod); + clazz.addMethod(wmethod); + } + + private CtMethod findOriginal(CtMethod m, boolean dontSearch) + throws NotFoundException + { + if (dontSearch) + return m; + + String name = m.getName(); + CtMethod[] ms = m.getDeclaringClass().getDeclaredMethods(); + for (int i = 0; i < ms.length; ++i) { + String orgName = ms[i].getName(); + if (orgName.endsWith(name) + && orgName.startsWith(ClassMetaobject.methodPrefix) + && ms[i].getSignature().equals(m.getSignature())) + return ms[i]; + } + + return m; + } + + private void processFields(CtClass clazz) + throws CannotCompileException, NotFoundException + { + CtField[] fs = clazz.getDeclaredFields(); + for (int i = 0; i < fs.length; ++i) { + CtField f = fs[i]; + int mod = f.getModifiers(); + if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { + mod |= Modifier.STATIC; + String name = f.getName(); + CtClass ftype = f.getType(); + CtMethod wmethod + = CtNewMethod.wrapped(ftype, readPrefix + name, + readParam, null, trapRead, + ConstParameter.string(name), + clazz); + wmethod.setModifiers(mod); + clazz.addMethod(wmethod); + CtClass[] writeParam = new CtClass[2]; + writeParam[0] = classPool.get("java.lang.Object"); + writeParam[1] = ftype; + wmethod = CtNewMethod.wrapped(CtClass.voidType, + writePrefix + name, + writeParam, null, trapWrite, + ConstParameter.string(name), clazz); + wmethod.setModifiers(mod); + clazz.addMethod(wmethod); + } + } + } + + public void rebuildClassFile(ClassFile cf) throws BadBytecode { + if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_6) + return; + + Iterator methods = cf.getMethods().iterator(); + while (methods.hasNext()) { + MethodInfo mi = (MethodInfo)methods.next(); + mi.rebuildStackMap(classPool); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Sample.java b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Sample.java new file mode 100644 index 0000000..9622be5 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Sample.java @@ -0,0 +1,57 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.reflect; + +/** + * A template used for defining a reflective class. + */ +public class Sample { + private Metaobject _metaobject; + private static ClassMetaobject _classobject; + + public Object trap(Object[] args, int identifier) throws Throwable { + Metaobject mobj; + mobj = _metaobject; + if (mobj == null) + return ClassMetaobject.invoke(this, identifier, args); + else + return mobj.trapMethodcall(identifier, args); + } + + public static Object trapStatic(Object[] args, int identifier) + throws Throwable + { + return _classobject.trapMethodcall(identifier, args); + } + + public static Object trapRead(Object[] args, String name) { + if (args[0] == null) + return _classobject.trapFieldRead(name); + else + return ((Metalevel)args[0])._getMetaobject().trapFieldRead(name); + } + + public static Object trapWrite(Object[] args, String name) { + Metalevel base = (Metalevel)args[0]; + if (base == null) + _classobject.trapFieldWrite(name, args[1]); + else + base._getMetaobject().trapFieldWrite(name, args[1]); + + return null; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/package.html b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/package.html new file mode 100644 index 0000000..10a4196 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/reflect/package.html @@ -0,0 +1,35 @@ + + +Runtime Behavioral Reflection. + +

(also recently known as interceptors or AOP?) + +

This package enables a metaobject to trap method calls and field +accesses on a regular Java object. It provides a class +Reflection, which is a main module for implementing +runtime behavioral reflection. +It also provides +a class Loader and Compiler +as utilities for dynamically or statically +translating a regular class into a reflective class. + +

An instance of the reflective class is associated with +a runtime metaobject and a runtime class metaobject, which control +the behavior of that instance. +The runtime +metaobject is created for every (base-level) instance but the +runtime class metaobject is created for every (base-level) class. +Metaobject is the root class of the runtime +metaobject and ClassMetaobject is the root class +of the runtime class metaobject. + +

This package is provided as a sample implementation of the +reflection mechanism with Javassist. All the programs in this package +uses only the regular Javassist API; they never call any hidden +methods. + +

The most significant class in this package is Reflection. +See the description of this class first. + + + diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java new file mode 100644 index 0000000..4ffcc7a --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java @@ -0,0 +1,251 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.rmi; + +import java.io.*; + +import com.wenshuo.agent.javassist.tools.web.*; +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.ClassPool; +import java.lang.reflect.Method; +import java.util.Hashtable; +import java.util.Vector; + +/** + * An AppletServer object is a web server that an ObjectImporter + * communicates with. It makes the objects specified by + * exportObject() remotely accessible from applets. + * If the classes of the exported objects are requested by the client-side + * JVM, this web server sends proxy classes for the requested classes. + * + * @see javassist.tools.rmi.ObjectImporter + */ +public class AppletServer extends Webserver { + private StubGenerator stubGen; + private Hashtable exportedNames; + private Vector exportedObjects; + + private static final byte[] okHeader + = "HTTP/1.0 200 OK\r\n\r\n".getBytes(); + + /** + * Constructs a web server. + * + * @param port port number + */ + public AppletServer(String port) + throws IOException, NotFoundException, CannotCompileException + { + this(Integer.parseInt(port)); + } + + /** + * Constructs a web server. + * + * @param port port number + */ + public AppletServer(int port) + throws IOException, NotFoundException, CannotCompileException + { + this(ClassPool.getDefault(), new StubGenerator(), port); + } + + /** + * Constructs a web server. + * + * @param port port number + * @param src the source of classs files. + */ + public AppletServer(int port, ClassPool src) + throws IOException, NotFoundException, CannotCompileException + { + this(new ClassPool(src), new StubGenerator(), port); + } + + private AppletServer(ClassPool loader, StubGenerator gen, int port) + throws IOException, NotFoundException, CannotCompileException + { + super(port); + exportedNames = new Hashtable(); + exportedObjects = new Vector(); + stubGen = gen; + addTranslator(loader, gen); + } + + /** + * Begins the HTTP service. + */ + public void run() { + super.run(); + } + + /** + * Exports an object. + * This method produces the bytecode of the proxy class used + * to access the exported object. A remote applet can load + * the proxy class and call a method on the exported object. + * + * @param name the name used for looking the object up. + * @param obj the exported object. + * @return the object identifier + * + * @see javassist.tools.rmi.ObjectImporter#lookupObject(String) + */ + public synchronized int exportObject(String name, Object obj) + throws CannotCompileException + { + Class clazz = obj.getClass(); + ExportedObject eo = new ExportedObject(); + eo.object = obj; + eo.methods = clazz.getMethods(); + exportedObjects.addElement(eo); + eo.identifier = exportedObjects.size() - 1; + if (name != null) + exportedNames.put(name, eo); + + try { + stubGen.makeProxyClass(clazz); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + + return eo.identifier; + } + + /** + * Processes a request from a web browser (an ObjectImporter). + */ + public void doReply(InputStream in, OutputStream out, String cmd) + throws IOException, BadHttpRequest + { + if (cmd.startsWith("POST /rmi ")) + processRMI(in, out); + else if (cmd.startsWith("POST /lookup ")) + lookupName(cmd, in, out); + else + super.doReply(in, out, cmd); + } + + private void processRMI(InputStream ins, OutputStream outs) + throws IOException + { + ObjectInputStream in = new ObjectInputStream(ins); + + int objectId = in.readInt(); + int methodId = in.readInt(); + Exception err = null; + Object rvalue = null; + try { + ExportedObject eo + = (ExportedObject)exportedObjects.elementAt(objectId); + Object[] args = readParameters(in); + rvalue = convertRvalue(eo.methods[methodId].invoke(eo.object, + args)); + } + catch(Exception e) { + err = e; + logging2(e.toString()); + } + + outs.write(okHeader); + ObjectOutputStream out = new ObjectOutputStream(outs); + if (err != null) { + out.writeBoolean(false); + out.writeUTF(err.toString()); + } + else + try { + out.writeBoolean(true); + out.writeObject(rvalue); + } + catch (NotSerializableException e) { + logging2(e.toString()); + } + catch (InvalidClassException e) { + logging2(e.toString()); + } + + out.flush(); + out.close(); + in.close(); + } + + private Object[] readParameters(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + int n = in.readInt(); + Object[] args = new Object[n]; + for (int i = 0; i < n; ++i) { + Object a = in.readObject(); + if (a instanceof RemoteRef) { + RemoteRef ref = (RemoteRef)a; + ExportedObject eo + = (ExportedObject)exportedObjects.elementAt(ref.oid); + a = eo.object; + } + + args[i] = a; + } + + return args; + } + + private Object convertRvalue(Object rvalue) + throws CannotCompileException + { + if (rvalue == null) + return null; // the return type is void. + + String classname = rvalue.getClass().getName(); + if (stubGen.isProxyClass(classname)) + return new RemoteRef(exportObject(null, rvalue), classname); + else + return rvalue; + } + + private void lookupName(String cmd, InputStream ins, OutputStream outs) + throws IOException + { + ObjectInputStream in = new ObjectInputStream(ins); + String name = DataInputStream.readUTF(in); + ExportedObject found = (ExportedObject)exportedNames.get(name); + outs.write(okHeader); + ObjectOutputStream out = new ObjectOutputStream(outs); + if (found == null) { + logging2(name + "not found."); + out.writeInt(-1); // error code + out.writeUTF("error"); + } + else { + logging2(name); + out.writeInt(found.identifier); + out.writeUTF(found.object.getClass().getName()); + } + + out.flush(); + out.close(); + in.close(); + } +} + +class ExportedObject { + public int identifier; + public Object object; + public Method[] methods; +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java new file mode 100644 index 0000000..776160e --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java @@ -0,0 +1,299 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.rmi; + +import java.io.*; +import java.net.*; +import java.applet.Applet; +import java.lang.reflect.*; + +/** + * The object importer enables applets to call a method on a remote + * object running on the Webserver (the main class of this + * package). + * + *

To access the remote + * object, the applet first calls lookupObject() and + * obtains a proxy object, which is a reference to that object. + * The class name of the proxy object is identical to that of + * the remote object. + * The proxy object provides the same set of methods as the remote object. + * If one of the methods is invoked on the proxy object, + * the invocation is delegated to the remote object. + * From the viewpoint of the applet, therefore, the two objects are + * identical. The applet can access the object on the server + * with the regular Java syntax without concern about the actual + * location. + * + *

The methods remotely called by the applet must be public. + * This is true even if the applet's class and the remote object's classs + * belong to the same package. + * + *

If class X is a class of remote objects, a subclass of X must be + * also a class of remote objects. On the other hand, this restriction + * is not applied to the superclass of X. The class X does not have to + * contain a constructor taking no arguments. + * + *

The parameters to a remote method is passed in the call-by-value + * manner. Thus all the parameter classes must implement + * java.io.Serializable. However, if the parameter is the + * proxy object, the reference to the remote object instead of a copy of + * the object is passed to the method. + * + *

Because of the limitations of the current implementation, + *

    + *
  • The parameter objects cannot contain the proxy + * object as a field value. + *
  • If class C is of the remote object, then + * the applet cannot instantiate C locally or remotely. + *
+ * + *

All the exceptions thrown by the remote object are converted + * into RemoteException. Since this exception is a subclass + * of RuntimeException, the caller method does not need + * to catch the exception. However, good programs should catch + * the RuntimeException. + * + * @see javassist.tools.rmi.AppletServer + * @see javassist.tools.rmi.RemoteException + * @see javassist.tools.web.Viewer + */ +public class ObjectImporter implements java.io.Serializable { + private final byte[] endofline = { 0x0d, 0x0a }; + private String servername, orgServername; + private int port, orgPort; + + protected byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes(); + protected byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes(); + + /** + * Constructs an object importer. + * + *

Remote objects are imported from the web server that the given + * applet has been loaded from. + * + * @param applet the applet loaded from the Webserver. + */ + public ObjectImporter(Applet applet) { + URL codebase = applet.getCodeBase(); + orgServername = servername = codebase.getHost(); + orgPort = port = codebase.getPort(); + } + + /** + * Constructs an object importer. + * + *

If you run a program with javassist.tools.web.Viewer, + * you can construct an object importer as follows: + * + *

+     * Viewer v = (Viewer)this.getClass().getClassLoader();
+     * ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort());
+     * 
+ * + * @see javassist.tools.web.Viewer + */ + public ObjectImporter(String servername, int port) { + this.orgServername = this.servername = servername; + this.orgPort = this.port = port; + } + + /** + * Finds the object exported by a server with the specified name. + * If the object is not found, this method returns null. + * + * @param name the name of the exported object. + * @return the proxy object or null. + */ + public Object getObject(String name) { + try { + return lookupObject(name); + } + catch (ObjectNotFoundException e) { + return null; + } + } + + /** + * Sets an http proxy server. After this method is called, the object + * importer connects a server through the http proxy server. + */ + public void setHttpProxy(String host, int port) { + String proxyHeader = "POST http://" + orgServername + ":" + orgPort; + String cmd = proxyHeader + "/lookup HTTP/1.0"; + lookupCommand = cmd.getBytes(); + cmd = proxyHeader + "/rmi HTTP/1.0"; + rmiCommand = cmd.getBytes(); + this.servername = host; + this.port = port; + } + + /** + * Finds the object exported by the server with the specified name. + * It sends a POST request to the server (via an http proxy server + * if needed). + * + * @param name the name of the exported object. + * @return the proxy object. + */ + public Object lookupObject(String name) throws ObjectNotFoundException + { + try { + Socket sock = new Socket(servername, port); + OutputStream out = sock.getOutputStream(); + out.write(lookupCommand); + out.write(endofline); + out.write(endofline); + + ObjectOutputStream dout = new ObjectOutputStream(out); + dout.writeUTF(name); + dout.flush(); + + InputStream in = new BufferedInputStream(sock.getInputStream()); + skipHeader(in); + ObjectInputStream din = new ObjectInputStream(in); + int n = din.readInt(); + String classname = din.readUTF(); + din.close(); + dout.close(); + sock.close(); + + if (n >= 0) + return createProxy(n, classname); + } + catch (Exception e) { + e.printStackTrace(); + throw new ObjectNotFoundException(name, e); + } + + throw new ObjectNotFoundException(name); + } + + private static final Class[] proxyConstructorParamTypes + = new Class[] { ObjectImporter.class, int.class }; + + private Object createProxy(int oid, String classname) throws Exception { + Class c = Class.forName(classname); + Constructor cons = c.getConstructor(proxyConstructorParamTypes); + return cons.newInstance(new Object[] { this, new Integer(oid) }); + } + + /** + * Calls a method on a remote object. + * It sends a POST request to the server (via an http proxy server + * if needed). + * + *

This method is called by only proxy objects. + */ + public Object call(int objectid, int methodid, Object[] args) + throws RemoteException + { + boolean result; + Object rvalue; + String errmsg; + + try { + /* This method establishes a raw tcp connection for sending + * a POST message. Thus the object cannot communicate a + * remote object beyond a fire wall. To avoid this problem, + * the connection should be established with a mechanism + * collaborating a proxy server. Unfortunately, java.lang.URL + * does not seem to provide such a mechanism. + * + * You might think that using HttpURLConnection is a better + * way than constructing a raw tcp connection. Unfortunately, + * URL.openConnection() does not return an HttpURLConnection + * object in Netscape's JVM. It returns a + * netscape.net.URLConnection object. + * + * lookupObject() has the same problem. + */ + Socket sock = new Socket(servername, port); + OutputStream out = new BufferedOutputStream( + sock.getOutputStream()); + out.write(rmiCommand); + out.write(endofline); + out.write(endofline); + + ObjectOutputStream dout = new ObjectOutputStream(out); + dout.writeInt(objectid); + dout.writeInt(methodid); + writeParameters(dout, args); + dout.flush(); + + InputStream ins = new BufferedInputStream(sock.getInputStream()); + skipHeader(ins); + ObjectInputStream din = new ObjectInputStream(ins); + result = din.readBoolean(); + rvalue = null; + errmsg = null; + if (result) + rvalue = din.readObject(); + else + errmsg = din.readUTF(); + + din.close(); + dout.close(); + sock.close(); + + if (rvalue instanceof RemoteRef) { + RemoteRef ref = (RemoteRef)rvalue; + rvalue = createProxy(ref.oid, ref.classname); + } + } + catch (ClassNotFoundException e) { + throw new RemoteException(e); + } + catch (IOException e) { + throw new RemoteException(e); + } + catch (Exception e) { + throw new RemoteException(e); + } + + if (result) + return rvalue; + else + throw new RemoteException(errmsg); + } + + private void skipHeader(InputStream in) throws IOException { + int len; + do { + int c; + len = 0; + while ((c = in.read()) >= 0 && c != 0x0d) + ++len; + + in.read(); /* skip 0x0a (LF) */ + } while (len > 0); + } + + private void writeParameters(ObjectOutputStream dout, Object[] params) + throws IOException + { + int n = params.length; + dout.writeInt(n); + for (int i = 0; i < n; ++i) + if (params[i] instanceof Proxy) { + Proxy p = (Proxy)params[i]; + dout.writeObject(new RemoteRef(p._getObjectId())); + } + else + dout.writeObject(params[i]); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java new file mode 100644 index 0000000..cc4f801 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java @@ -0,0 +1,27 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.rmi; + +public class ObjectNotFoundException extends Exception { + public ObjectNotFoundException(String name) { + super(name + " is not exported"); + } + + public ObjectNotFoundException(String name, Exception e) { + super(name + " because of " + e.toString()); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Proxy.java b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Proxy.java new file mode 100644 index 0000000..9be28be --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Proxy.java @@ -0,0 +1,26 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.rmi; + +/** + * An interface implemented by proxy classes. + * + * @see javassist.tools.rmi.StubGenerator + */ +public interface Proxy { + int _getObjectId(); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java new file mode 100644 index 0000000..9d6a3c7 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java @@ -0,0 +1,31 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.rmi; + +/** + * RemoteException represents any exception thrown + * during remote method invocation. + */ +public class RemoteException extends RuntimeException { + public RemoteException(String msg) { + super(msg); + } + + public RemoteException(Exception e) { + super("by " + e.toString()); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java new file mode 100644 index 0000000..a35a417 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java @@ -0,0 +1,36 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.rmi; + +/** + * Remote reference. This class is internally used for sending a remote + * reference through a network stream. + */ +public class RemoteRef implements java.io.Serializable { + public int oid; + public String classname; + + public RemoteRef(int i) { + oid = i; + classname = null; + } + + public RemoteRef(int i, String name) { + oid = i; + classname = name; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Sample.java b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Sample.java new file mode 100644 index 0000000..409e959 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Sample.java @@ -0,0 +1,37 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.rmi; + +/** + * A template used for defining a proxy class. + * The class file of this class is read by the StubGenerator + * class. + */ +public class Sample { + private ObjectImporter importer; + private int objectId; + + public Object forward(Object[] args, int identifier) { + return importer.call(objectId, identifier, args); + } + + public static Object forwardStatic(Object[] args, int identifier) + throws RemoteException + { + throw new RemoteException("cannot call a static method."); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java new file mode 100644 index 0000000..9eaf6bf --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java @@ -0,0 +1,256 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.rmi; + +import com.wenshuo.agent.javassist.*; +import java.lang.reflect.Method; +import java.util.Hashtable; +import com.wenshuo.agent.javassist.CtMethod.ConstParameter; + +/** + * A stub-code generator. It is used for producing a proxy class. + * + *

The proxy class for class A is as follows: + * + *

public class A implements Proxy, Serializable {
+ *   private ObjectImporter importer;
+ *   private int objectId;
+ *   public int _getObjectId() { return objectId; }
+ *   public A(ObjectImporter oi, int id) {
+ *     importer = oi; objectId = id;
+ *   }
+ *
+ *   ... the same methods that the original class A declares ...
+ * }
+ * + *

Instances of the proxy class is created by an + * ObjectImporter object. + */ +public class StubGenerator implements Translator { + private static final String fieldImporter = "importer"; + private static final String fieldObjectId = "objectId"; + private static final String accessorObjectId = "_getObjectId"; + private static final String sampleClass = "javassist.tools.rmi.Sample"; + + private ClassPool classPool; + private Hashtable proxyClasses; + private CtMethod forwardMethod; + private CtMethod forwardStaticMethod; + + private CtClass[] proxyConstructorParamTypes; + private CtClass[] interfacesForProxy; + private CtClass[] exceptionForProxy; + + /** + * Constructs a stub-code generator. + */ + public StubGenerator() { + proxyClasses = new Hashtable(); + } + + /** + * Initializes the object. + * This is a method declared in javassist.Translator. + * + * @see javassist.Translator#start(ClassPool) + */ + public void start(ClassPool pool) throws NotFoundException { + classPool = pool; + CtClass c = pool.get(sampleClass); + forwardMethod = c.getDeclaredMethod("forward"); + forwardStaticMethod = c.getDeclaredMethod("forwardStatic"); + + proxyConstructorParamTypes + = pool.get(new String[] { "javassist.tools.rmi.ObjectImporter", + "int" }); + interfacesForProxy + = pool.get(new String[] { "java.io.Serializable", + "javassist.tools.rmi.Proxy" }); + exceptionForProxy + = new CtClass[] { pool.get("javassist.tools.rmi.RemoteException") }; + } + + /** + * Does nothing. + * This is a method declared in javassist.Translator. + * @see javassist.Translator#onLoad(ClassPool,String) + */ + public void onLoad(ClassPool pool, String classname) {} + + /** + * Returns true if the specified class is a proxy class + * recorded by makeProxyClass(). + * + * @param name a fully-qualified class name + */ + public boolean isProxyClass(String name) { + return proxyClasses.get(name) != null; + } + + /** + * Makes a proxy class. The produced class is substituted + * for the original class. + * + * @param clazz the class referenced + * through the proxy class. + * @return false if the proxy class + * has been already produced. + */ + public synchronized boolean makeProxyClass(Class clazz) + throws CannotCompileException, NotFoundException + { + String classname = clazz.getName(); + if (proxyClasses.get(classname) != null) + return false; + else { + CtClass ctclazz = produceProxyClass(classPool.get(classname), + clazz); + proxyClasses.put(classname, ctclazz); + modifySuperclass(ctclazz); + return true; + } + } + + private CtClass produceProxyClass(CtClass orgclass, Class orgRtClass) + throws CannotCompileException, NotFoundException + { + int modify = orgclass.getModifiers(); + if (Modifier.isAbstract(modify) || Modifier.isNative(modify) + || !Modifier.isPublic(modify)) + throw new CannotCompileException(orgclass.getName() + + " must be public, non-native, and non-abstract."); + + CtClass proxy = classPool.makeClass(orgclass.getName(), + orgclass.getSuperclass()); + + proxy.setInterfaces(interfacesForProxy); + + CtField f + = new CtField(classPool.get("javassist.tools.rmi.ObjectImporter"), + fieldImporter, proxy); + f.setModifiers(Modifier.PRIVATE); + proxy.addField(f, CtField.Initializer.byParameter(0)); + + f = new CtField(CtClass.intType, fieldObjectId, proxy); + f.setModifiers(Modifier.PRIVATE); + proxy.addField(f, CtField.Initializer.byParameter(1)); + + proxy.addMethod(CtNewMethod.getter(accessorObjectId, f)); + + proxy.addConstructor(CtNewConstructor.defaultConstructor(proxy)); + CtConstructor cons + = CtNewConstructor.skeleton(proxyConstructorParamTypes, + null, proxy); + proxy.addConstructor(cons); + + try { + addMethods(proxy, orgRtClass.getMethods()); + return proxy; + } + catch (SecurityException e) { + throw new CannotCompileException(e); + } + } + + private CtClass toCtClass(Class rtclass) throws NotFoundException { + String name; + if (!rtclass.isArray()) + name = rtclass.getName(); + else { + StringBuffer sbuf = new StringBuffer(); + do { + sbuf.append("[]"); + rtclass = rtclass.getComponentType(); + } while(rtclass.isArray()); + sbuf.insert(0, rtclass.getName()); + name = sbuf.toString(); + } + + return classPool.get(name); + } + + private CtClass[] toCtClass(Class[] rtclasses) throws NotFoundException { + int n = rtclasses.length; + CtClass[] ctclasses = new CtClass[n]; + for (int i = 0; i < n; ++i) + ctclasses[i] = toCtClass(rtclasses[i]); + + return ctclasses; + } + + /* ms must not be an array of CtMethod. To invoke a method ms[i] + * on a server, a client must send i to the server. + */ + private void addMethods(CtClass proxy, Method[] ms) + throws CannotCompileException, NotFoundException + { + CtMethod wmethod; + for (int i = 0; i < ms.length; ++i) { + Method m = ms[i]; + int mod = m.getModifiers(); + if (m.getDeclaringClass() != Object.class + && !Modifier.isFinal(mod)) + if (Modifier.isPublic(mod)) { + CtMethod body; + if (Modifier.isStatic(mod)) + body = forwardStaticMethod; + else + body = forwardMethod; + + wmethod + = CtNewMethod.wrapped(toCtClass(m.getReturnType()), + m.getName(), + toCtClass(m.getParameterTypes()), + exceptionForProxy, + body, + ConstParameter.integer(i), + proxy); + wmethod.setModifiers(mod); + proxy.addMethod(wmethod); + } + else if (!Modifier.isProtected(mod) + && !Modifier.isPrivate(mod)) + // if package method + throw new CannotCompileException( + "the methods must be public, protected, or private."); + } + } + + /** + * Adds a default constructor to the super classes. + */ + private void modifySuperclass(CtClass orgclass) + throws CannotCompileException, NotFoundException + { + CtClass superclazz; + for (;; orgclass = superclazz) { + superclazz = orgclass.getSuperclass(); + if (superclazz == null) + break; + + try { + superclazz.getDeclaredConstructor(null); + break; // the constructor with no arguments is found. + } + catch (NotFoundException e) { + } + + superclazz.addConstructor( + CtNewConstructor.defaultConstructor(superclazz)); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/package.html b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/package.html new file mode 100644 index 0000000..5432a94 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/rmi/package.html @@ -0,0 +1,16 @@ + + +Sample implementation of remote method invocation. + +

This package enables applets to access remote objects +running on the web server with regular Java syntax. +It is provided as a sample implementation with Javassist. +All the programs in this package uses only the regular +Javassist API; they never call any hidden methods. + +

The most significant class of this package is +ObjectImporter. +See the description of this class first. + + + diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java b/src/main/java/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java new file mode 100644 index 0000000..93cf67c --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java @@ -0,0 +1,35 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.web; + +/** + * Thrown when receiving an invalid HTTP request. + */ +public class BadHttpRequest extends Exception { + private Exception e; + + public BadHttpRequest() { e = null; } + + public BadHttpRequest(Exception _e) { e = _e; } + + public String toString() { + if (e == null) + return super.toString(); + else + return e.toString(); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/web/Viewer.java b/src/main/java/com/wenshuo/agent/javassist/tools/web/Viewer.java new file mode 100644 index 0000000..52d5416 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/web/Viewer.java @@ -0,0 +1,209 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.web; + +import java.io.*; +import java.net.*; + +/** + * A sample applet viewer. + * + *

This is a sort of applet viewer that can run any program even if + * the main class is not a subclass of Applet. + * This viewwer first calls main() in the main class. + * + *

To run, you should type: + * + *

% java javassist.tools.web.Viewer host port Main arg1, ...
+ * + *

This command calls Main.main() with arg1,... + * All classes including Main are fetched from + * a server http://host:port. + * Only the class file for Viewer must exist + * on a local file system at the client side; even other + * javassist.* classes are not needed at the client side. + * Viewer uses only Java core API classes. + * + *

Note: since a Viewer object is a class loader, + * a program loaded by this object can call a method in Viewer. + * For example, you can write something like this: + * + *

+ * Viewer v = (Viewer)this.getClass().getClassLoader();
+ * String port = v.getPort();
+ * 
+ * + */ +public class Viewer extends ClassLoader { + private String server; + private int port; + + /** + * Starts a program. + */ + public static void main(String[] args) throws Throwable { + if (args.length >= 3) { + Viewer cl = new Viewer(args[0], Integer.parseInt(args[1])); + String[] args2 = new String[args.length - 3]; + System.arraycopy(args, 3, args2, 0, args.length - 3); + cl.run(args[2], args2); + } + else + System.err.println( + "Usage: java javassist.tools.web.Viewer class [args ...]"); + } + + /** + * Constructs a viewer. + * + * @param host server name + * @param p port number + */ + public Viewer(String host, int p) { + server = host; + port = p; + } + + /** + * Returns the server name. + */ + public String getServer() { return server; } + + /** + * Returns the port number. + */ + public int getPort() { return port; } + + /** + * Invokes main() in the class specified by classname. + * + * @param classname executed class + * @param args the arguments passed to main(). + */ + public void run(String classname, String[] args) + throws Throwable + { + Class c = loadClass(classname); + try { + c.getDeclaredMethod("main", new Class[] { String[].class }) + .invoke(null, new Object[] { args }); + } + catch (java.lang.reflect.InvocationTargetException e) { + throw e.getTargetException(); + } + } + + /** + * Requests the class loader to load a class. + */ + protected synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + Class c = findLoadedClass(name); + if (c == null) + c = findClass(name); + + if (c == null) + throw new ClassNotFoundException(name); + + if (resolve) + resolveClass(c); + + return c; + } + + /** + * Finds the specified class. The implementation in this class + * fetches the class from the http server. If the class is + * either java.*, javax.*, or + * Viewer, then it is loaded by the parent class + * loader. + * + *

This method can be overridden by a subclass of + * Viewer. + */ + protected Class findClass(String name) throws ClassNotFoundException { + Class c = null; + if (name.startsWith("java.") || name.startsWith("javax.") + || name.equals("javassist.tools.web.Viewer")) + c = findSystemClass(name); + + if (c == null) + try { + byte[] b = fetchClass(name); + if (b != null) + c = defineClass(name, b, 0, b.length); + } + catch (Exception e) { + } + + return c; + } + + /** + * Fetches the class file of the specified class from the http + * server. + */ + protected byte[] fetchClass(String classname) throws Exception + { + byte[] b; + URL url = new URL("http", server, port, + "/" + classname.replace('.', '/') + ".class"); + URLConnection con = url.openConnection(); + con.connect(); + int size = con.getContentLength(); + InputStream s = con.getInputStream(); + if (size <= 0) + b = readStream(s); + else { + b = new byte[size]; + int len = 0; + do { + int n = s.read(b, len, size - len); + if (n < 0) { + s.close(); + throw new IOException("the stream was closed: " + + classname); + } + len += n; + } while (len < size); + } + + s.close(); + return b; + } + + private byte[] readStream(InputStream fin) throws IOException { + byte[] buf = new byte[4096]; + int size = 0; + int len = 0; + do { + size += len; + if (buf.length - size <= 0) { + byte[] newbuf = new byte[buf.length * 2]; + System.arraycopy(buf, 0, newbuf, 0, size); + buf = newbuf; + } + + len = fin.read(buf, size, buf.length - size); + } while (len >= 0); + + byte[] result = new byte[size]; + System.arraycopy(buf, 0, result, 0, size); + return result; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/web/Webserver.java b/src/main/java/com/wenshuo/agent/javassist/tools/web/Webserver.java new file mode 100644 index 0000000..5511ef2 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/web/Webserver.java @@ -0,0 +1,408 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.tools.web; + +import java.net.*; +import java.io.*; +import java.util.Date; +import com.wenshuo.agent.javassist.*; + +/** + * A web server for running sample programs. + * + *

This enables a Java program to instrument class files loaded by + * web browsers for applets. Since the (standard) security manager + * does not allow an applet to create and use a class loader, + * instrumenting class files must be done by this web server. + * + *

Note: although this class is included in the Javassist API, + * it is provided as a sample implementation of the web server using + * Javassist. Especially, there might be security flaws in this server. + * Please use this with YOUR OWN RISK. + */ +public class Webserver { + private ServerSocket socket; + private ClassPool classPool; + protected Translator translator; + + private final static byte[] endofline = { 0x0d, 0x0a }; + + private final static int typeHtml = 1; + private final static int typeClass = 2; + private final static int typeGif = 3; + private final static int typeJpeg = 4; + private final static int typeText = 5; + + /** + * If this field is not null, the class files taken from + * ClassPool are written out under the directory + * specified by this field. The directory name must not end + * with a directory separator. + */ + public String debugDir = null; + + /** + * The top directory of html (and .gif, .class, ...) files. + * It must end with the directory separator such as "/". + * (For portability, "/" should be used as the directory separator. + * Javassist automatically translates "/" into a platform-dependent + * character.) + * If this field is null, the top directory is the current one where + * the JVM is running. + * + *

If the given URL indicates a class file and the class file + * is not found under the directory specified by this variable, + * then Class.getResourceAsStream() is called + * for searching the Java class paths. + */ + public String htmlfileBase = null; + + /** + * Starts a web server. + * The port number is specified by the first argument. + */ + public static void main(String[] args) throws IOException { + if (args.length == 1) { + Webserver web = new Webserver(args[0]); + web.run(); + } + else + System.err.println( + "Usage: java javassist.tools.web.Webserver "); + } + + /** + * Constructs a web server. + * + * @param port port number + */ + public Webserver(String port) throws IOException { + this(Integer.parseInt(port)); + } + + /** + * Constructs a web server. + * + * @param port port number + */ + public Webserver(int port) throws IOException { + socket = new ServerSocket(port); + classPool = null; + translator = null; + } + + /** + * Requests the web server to use the specified + * ClassPool object for obtaining a class file. + */ + public void setClassPool(ClassPool loader) { + classPool = loader; + } + + /** + * Adds a translator, which is called whenever a client requests + * a class file. + * + * @param cp the ClassPool object for obtaining + * a class file. + * @param t a translator. + */ + public void addTranslator(ClassPool cp, Translator t) + throws NotFoundException, CannotCompileException + { + classPool = cp; + translator = t; + t.start(classPool); + } + + /** + * Closes the socket. + */ + public void end() throws IOException { + socket.close(); + } + + /** + * Prints a log message. + */ + public void logging(String msg) { + System.out.println(msg); + } + + /** + * Prints a log message. + */ + public void logging(String msg1, String msg2) { + System.out.print(msg1); + System.out.print(" "); + System.out.println(msg2); + } + + /** + * Prints a log message. + */ + public void logging(String msg1, String msg2, String msg3) { + System.out.print(msg1); + System.out.print(" "); + System.out.print(msg2); + System.out.print(" "); + System.out.println(msg3); + } + + /** + * Prints a log message with indentation. + */ + public void logging2(String msg) { + System.out.print(" "); + System.out.println(msg); + } + + /** + * Begins the HTTP service. + */ + public void run() { + System.err.println("ready to service..."); + for (;;) + try { + ServiceThread th = new ServiceThread(this, socket.accept()); + th.start(); + } + catch (IOException e) { + logging(e.toString()); + } + } + + final void process(Socket clnt) throws IOException { + InputStream in = new BufferedInputStream(clnt.getInputStream()); + String cmd = readLine(in); + logging(clnt.getInetAddress().getHostName(), + new Date().toString(), cmd); + while (skipLine(in) > 0){ + } + + OutputStream out = new BufferedOutputStream(clnt.getOutputStream()); + try { + doReply(in, out, cmd); + } + catch (BadHttpRequest e) { + replyError(out, e); + } + + out.flush(); + in.close(); + out.close(); + clnt.close(); + } + + private String readLine(InputStream in) throws IOException { + StringBuffer buf = new StringBuffer(); + int c; + while ((c = in.read()) >= 0 && c != 0x0d) + buf.append((char)c); + + in.read(); /* skip 0x0a (LF) */ + return buf.toString(); + } + + private int skipLine(InputStream in) throws IOException { + int c; + int len = 0; + while ((c = in.read()) >= 0 && c != 0x0d) + ++len; + + in.read(); /* skip 0x0a (LF) */ + return len; + } + + /** + * Proceses a HTTP request from a client. + * + * @param out the output stream to a client + * @param cmd the command received from a client + */ + public void doReply(InputStream in, OutputStream out, String cmd) + throws IOException, BadHttpRequest + { + int len; + int fileType; + String filename, urlName; + + if (cmd.startsWith("GET /")) + filename = urlName = cmd.substring(5, cmd.indexOf(' ', 5)); + else + throw new BadHttpRequest(); + + if (filename.endsWith(".class")) + fileType = typeClass; + else if (filename.endsWith(".html") || filename.endsWith(".htm")) + fileType = typeHtml; + else if (filename.endsWith(".gif")) + fileType = typeGif; + else if (filename.endsWith(".jpg")) + fileType = typeJpeg; + else + fileType = typeText; // or textUnknown + + len = filename.length(); + if (fileType == typeClass + && letUsersSendClassfile(out, filename, len)) + return; + + checkFilename(filename, len); + if (htmlfileBase != null) + filename = htmlfileBase + filename; + + if (File.separatorChar != '/') + filename = filename.replace('/', File.separatorChar); + + File file = new File(filename); + if (file.canRead()) { + sendHeader(out, file.length(), fileType); + FileInputStream fin = new FileInputStream(file); + byte[] filebuffer = new byte[4096]; + for (;;) { + len = fin.read(filebuffer); + if (len <= 0) + break; + else + out.write(filebuffer, 0, len); + } + + fin.close(); + return; + } + + // If the file is not found under the html-file directory, + // then Class.getResourceAsStream() is tried. + + if (fileType == typeClass) { + InputStream fin + = getClass().getResourceAsStream("/" + urlName); + if (fin != null) { + ByteArrayOutputStream barray = new ByteArrayOutputStream(); + byte[] filebuffer = new byte[4096]; + for (;;) { + len = fin.read(filebuffer); + if (len <= 0) + break; + else + barray.write(filebuffer, 0, len); + } + + byte[] classfile = barray.toByteArray(); + sendHeader(out, classfile.length, typeClass); + out.write(classfile); + fin.close(); + return; + } + } + + throw new BadHttpRequest(); + } + + private void checkFilename(String filename, int len) + throws BadHttpRequest + { + for (int i = 0; i < len; ++i) { + char c = filename.charAt(i); + if (!Character.isJavaIdentifierPart(c) && c != '.' && c != '/') + throw new BadHttpRequest(); + } + + if (filename.indexOf("..") >= 0) + throw new BadHttpRequest(); + } + + private boolean letUsersSendClassfile(OutputStream out, + String filename, int length) + throws IOException, BadHttpRequest + { + if (classPool == null) + return false; + + byte[] classfile; + String classname + = filename.substring(0, length - 6).replace('/', '.'); + try { + if (translator != null) + translator.onLoad(classPool, classname); + + CtClass c = classPool.get(classname); + classfile = c.toBytecode(); + if (debugDir != null) + c.writeFile(debugDir); + } + catch (Exception e) { + throw new BadHttpRequest(e); + } + + sendHeader(out, classfile.length, typeClass); + out.write(classfile); + return true; + } + + private void sendHeader(OutputStream out, long dataLength, int filetype) + throws IOException + { + out.write("HTTP/1.0 200 OK".getBytes()); + out.write(endofline); + out.write("Content-Length: ".getBytes()); + out.write(Long.toString(dataLength).getBytes()); + out.write(endofline); + if (filetype == typeClass) + out.write("Content-Type: application/octet-stream".getBytes()); + else if (filetype == typeHtml) + out.write("Content-Type: text/html".getBytes()); + else if (filetype == typeGif) + out.write("Content-Type: image/gif".getBytes()); + else if (filetype == typeJpeg) + out.write("Content-Type: image/jpg".getBytes()); + else if (filetype == typeText) + out.write("Content-Type: text/plain".getBytes()); + + out.write(endofline); + out.write(endofline); + } + + private void replyError(OutputStream out, BadHttpRequest e) + throws IOException + { + logging2("bad request: " + e.toString()); + out.write("HTTP/1.0 400 Bad Request".getBytes()); + out.write(endofline); + out.write(endofline); + out.write("

Bad Request

".getBytes()); + } +} + +class ServiceThread extends Thread { + Webserver web; + Socket sock; + + public ServiceThread(Webserver w, Socket s) { + web = w; + sock = s; + } + + public void run() { + try { + web.process(sock); + } + catch (IOException e) { + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/web/package.html b/src/main/java/com/wenshuo/agent/javassist/tools/web/package.html new file mode 100644 index 0000000..0c7fb45 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/tools/web/package.html @@ -0,0 +1,7 @@ + + +Simple web server for running sample code. + +

This package provides a simple web server for sample packages. + + diff --git a/src/main/java/com/wenshuo/agent/javassist/util/HotSwapper.java b/src/main/java/com/wenshuo/agent/javassist/util/HotSwapper.java new file mode 100644 index 0000000..45836bf --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/HotSwapper.java @@ -0,0 +1,251 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util; + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; +import java.io.*; +import java.util.*; + +class Trigger { + void doSwap() {} +} + +/** + * A utility class for dynamically reloading a class by + * the Java Platform Debugger Architecture (JPDA), or HotSwap. + * It works only with JDK 1.4 and later. + * + *

Note: The new definition of the reloaded class must declare + * the same set of methods and fields as the original definition. The + * schema change between the original and new definitions is not allowed + * by the JPDA. + * + *

To use this class, the JVM must be launched with the following + * command line options: + * + *

For Java 1.4,
+ *

java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
+ *

For Java 5,
+ *

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
+ * + *

Note that 8000 is the port number used by HotSwapper. + * Any port number can be specified. Since HotSwapper does not + * launch another JVM for running a target application, this port number + * is used only for inter-thread communication. + * + *

Furthermore, JAVA_HOME/lib/tools.jar must be included + * in the class path. + * + *

Using HotSwapper is easy. See the following example: + * + *

+ * CtClass clazz = ...
+ * byte[] classFile = clazz.toBytecode();
+ * HotSwapper hs = new HostSwapper(8000);  // 8000 is a port number.
+ * hs.reload("Test", classFile);
+ * 
+ * + *

reload() + * first unload the Test class and load a new version of + * the Test class. + * classFile is a byte array containing the new contents of + * the class file for the Test class. The developers can + * repatedly call reload() on the same HotSwapper + * object so that they can reload a number of classes. + * + * @since 3.1 + */ +public class HotSwapper { + private VirtualMachine jvm; + private MethodEntryRequest request; + private Map newClassFiles; + + private Trigger trigger; + + private static final String HOST_NAME = "localhost"; + private static final String TRIGGER_NAME = Trigger.class.getName(); + + /** + * Connects to the JVM. + * + * @param port the port number used for the connection to the JVM. + */ + public HotSwapper(int port) + throws IOException, IllegalConnectorArgumentsException + { + this(Integer.toString(port)); + } + + /** + * Connects to the JVM. + * + * @param port the port number used for the connection to the JVM. + */ + public HotSwapper(String port) + throws IOException, IllegalConnectorArgumentsException + { + jvm = null; + request = null; + newClassFiles = null; + trigger = new Trigger(); + AttachingConnector connector + = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach"); + + Map arguments = connector.defaultArguments(); + ((Connector.Argument)arguments.get("hostname")).setValue(HOST_NAME); + ((Connector.Argument)arguments.get("port")).setValue(port); + jvm = connector.attach(arguments); + EventRequestManager manager = jvm.eventRequestManager(); + request = methodEntryRequests(manager, TRIGGER_NAME); + } + + private Connector findConnector(String connector) throws IOException { + List connectors = Bootstrap.virtualMachineManager().allConnectors(); + Iterator iter = connectors.iterator(); + while (iter.hasNext()) { + Connector con = (Connector)iter.next(); + if (con.name().equals(connector)) { + return con; + } + } + + throw new IOException("Not found: " + connector); + } + + private static MethodEntryRequest methodEntryRequests( + EventRequestManager manager, + String classpattern) { + MethodEntryRequest mereq = manager.createMethodEntryRequest(); + mereq.addClassFilter(classpattern); + mereq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + return mereq; + } + + /* Stops triggering a hotswapper when reload() is called. + */ + private void deleteEventRequest(EventRequestManager manager, + MethodEntryRequest request) { + manager.deleteEventRequest(request); + } + + /** + * Reloads a class. + * + * @param className the fully-qualified class name. + * @param classFile the contents of the class file. + */ + public void reload(String className, byte[] classFile) { + ReferenceType classtype = toRefType(className); + Map map = new HashMap(); + map.put(classtype, classFile); + reload2(map, className); + } + + /** + * Reloads a class. + * + * @param classFiles a map between fully-qualified class names + * and class files. The type of the class names + * is String and the type of the + * class files is byte[]. + */ + public void reload(Map classFiles) { + Set set = classFiles.entrySet(); + Iterator it = set.iterator(); + Map map = new HashMap(); + String className = null; + while (it.hasNext()) { + Map.Entry e = (Map.Entry)it.next(); + className = (String)e.getKey(); + map.put(toRefType(className), e.getValue()); + } + + if (className != null) + reload2(map, className + " etc."); + } + + private ReferenceType toRefType(String className) { + List list = jvm.classesByName(className); + if (list == null || list.isEmpty()) + throw new RuntimeException("no such class: " + className); + else + return (ReferenceType)list.get(0); + } + + private void reload2(Map map, String msg) { + synchronized (trigger) { + startDaemon(); + newClassFiles = map; + request.enable(); + trigger.doSwap(); + request.disable(); + Map ncf = newClassFiles; + if (ncf != null) { + newClassFiles = null; + throw new RuntimeException("failed to reload: " + msg); + } + } + } + + private void startDaemon() { + new Thread() { + private void errorMsg(Throwable e) { + System.err.print("Exception in thread \"HotSwap\" "); + e.printStackTrace(System.err); + } + + public void run() { + EventSet events = null; + try { + events = waitEvent(); + EventIterator iter = events.eventIterator(); + while (iter.hasNext()) { + Event event = iter.nextEvent(); + if (event instanceof MethodEntryEvent) { + hotswap(); + break; + } + } + } + catch (Throwable e) { + errorMsg(e); + } + try { + if (events != null) + events.resume(); + } + catch (Throwable e) { + errorMsg(e); + } + } + }.start(); + } + + EventSet waitEvent() throws InterruptedException { + EventQueue queue = jvm.eventQueue(); + return queue.remove(); + } + + void hotswap() { + Map map = newClassFiles; + jvm.redefineClasses(map); + newClassFiles = null; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/package.html b/src/main/java/com/wenshuo/agent/javassist/util/package.html new file mode 100644 index 0000000..349d996 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/package.html @@ -0,0 +1,5 @@ + + +Utility classes. + + diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java new file mode 100644 index 0000000..02f961e --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java @@ -0,0 +1,237 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +import java.lang.reflect.Method; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.ProtectionDomain; + +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.bytecode.ClassFile; + +/** + * A helper class for implementing ProxyFactory. + * The users of ProxyFactory do not have to see this class. + * + * @see ProxyFactory + */ +public class FactoryHelper { + private static java.lang.reflect.Method defineClass1, defineClass2; + + static { + try { + Class cl = Class.forName("java.lang.ClassLoader"); + defineClass1 = SecurityActions.getDeclaredMethod( + cl, + "defineClass", + new Class[] { String.class, byte[].class, + int.class, int.class }); + + defineClass2 = SecurityActions.getDeclaredMethod( + cl, + "defineClass", + new Class[] { String.class, byte[].class, + int.class, int.class, ProtectionDomain.class }); + } + catch (Exception e) { + throw new RuntimeException("cannot initialize"); + } + } + + /** + * Returns an index for accessing arrays in this class. + * + * @throws RuntimeException if a given type is not a primitive type. + */ + public static final int typeIndex(Class type) { + Class[] list = primitiveTypes; + int n = list.length; + for (int i = 0; i < n; i++) + if (list[i] == type) + return i; + + throw new RuntimeException("bad type:" + type.getName()); + } + + /** + * Class objects representing primitive types. + */ + public static final Class[] primitiveTypes = { + Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, + Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE + }; + + /** + * The fully-qualified names of wrapper classes for primitive types. + */ + public static final String[] wrapperTypes = { + "java.lang.Boolean", "java.lang.Byte", "java.lang.Character", + "java.lang.Short", "java.lang.Integer", "java.lang.Long", + "java.lang.Float", "java.lang.Double", "java.lang.Void" + }; + + /** + * The descriptors of the constructors of wrapper classes. + */ + public static final String[] wrapperDesc = { + "(Z)V", "(B)V", "(C)V", "(S)V", "(I)V", "(J)V", + "(F)V", "(D)V" + }; + + /** + * The names of methods for obtaining a primitive value + * from a wrapper object. For example, intValue() + * is such a method for obtaining an integer value from a + * java.lang.Integer object. + */ + public static final String[] unwarpMethods = { + "booleanValue", "byteValue", "charValue", "shortValue", + "intValue", "longValue", "floatValue", "doubleValue" + }; + + /** + * The descriptors of the unwrapping methods contained + * in unwrapMethods. + */ + public static final String[] unwrapDesc = { + "()Z", "()B", "()C", "()S", "()I", "()J", "()F", "()D" + }; + + /** + * The data size of primitive types. long + * and double are 2; the others are 1. + */ + public static final int[] dataSize = { + 1, 1, 1, 1, 1, 2, 1, 2 + }; + + /** + * Loads a class file by a given class loader. + * This method uses a default protection domain for the class + * but it may not work with a security manager or a sigend jar file. + * + * @see #toClass(ClassFile,ClassLoader,ProtectionDomain) + */ + public static Class toClass(ClassFile cf, ClassLoader loader) + throws CannotCompileException + { + return toClass(cf, loader, null); + } + + /** + * Loads a class file by a given class loader. + * + * @param domain if it is null, a default domain is used. + * @since 3.3 + */ + public static Class toClass(ClassFile cf, ClassLoader loader, ProtectionDomain domain) + throws CannotCompileException + { + try { + byte[] b = toBytecode(cf); + Method method; + Object[] args; + if (domain == null) { + method = defineClass1; + args = new Object[] { cf.getName(), b, new Integer(0), + new Integer(b.length) }; + } + else { + method = defineClass2; + args = new Object[] { cf.getName(), b, new Integer(0), + new Integer(b.length), domain }; + } + + return toClass2(method, loader, args); + } + catch (RuntimeException e) { + throw e; + } + catch (java.lang.reflect.InvocationTargetException e) { + throw new CannotCompileException(e.getTargetException()); + } + catch (Exception e) { + throw new CannotCompileException(e); + } + } + + private static synchronized Class toClass2(Method method, + ClassLoader loader, Object[] args) + throws Exception + { + SecurityActions.setAccessible(method, true); + Class clazz = (Class)method.invoke(loader, args); + SecurityActions.setAccessible(method, false); + return clazz; + } + + private static byte[] toBytecode(ClassFile cf) throws IOException { + ByteArrayOutputStream barray = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(barray); + try { + cf.write(out); + } + finally { + out.close(); + } + + return barray.toByteArray(); + } + + /** + * Writes a class file. + */ + public static void writeFile(ClassFile cf, String directoryName) + throws CannotCompileException { + try { + writeFile0(cf, directoryName); + } + catch (IOException e) { + throw new CannotCompileException(e); + } + } + + private static void writeFile0(ClassFile cf, String directoryName) + throws CannotCompileException, IOException { + String classname = cf.getName(); + String filename = directoryName + File.separatorChar + + classname.replace('.', File.separatorChar) + ".class"; + int pos = filename.lastIndexOf(File.separatorChar); + if (pos > 0) { + String dir = filename.substring(0, pos); + if (!dir.equals(".")) + new File(dir).mkdirs(); + } + + DataOutputStream out = new DataOutputStream(new BufferedOutputStream( + new FileOutputStream(filename))); + try { + cf.write(out); + } + catch (IOException e) { + throw e; + } + finally { + out.close(); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java new file mode 100644 index 0000000..4ebe168 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java @@ -0,0 +1,31 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +import java.lang.reflect.Method; + +/** + * Selector of the methods implemented by a handler. + * + * @see ProxyFactory#setFilter(MethodFilter) + */ +public interface MethodFilter { + /** + * Returns true if the given method is implemented by a handler. + */ + boolean isHandled(Method m); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java new file mode 100644 index 0000000..546dc76 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java @@ -0,0 +1,49 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +import java.lang.reflect.Method; + +/** + * The interface implemented by the invocation handler of a proxy + * instance. + * + * @see Proxy#setHandler(MethodHandler) + */ +public interface MethodHandler { + /** + * Is called when a method is invoked on a proxy instance associated + * with this handler. This method must process that method invocation. + * + * @param self the proxy instance. + * @param thisMethod the overridden method declared in the super + * class or interface. + * @param proceed the forwarder method for invoking the overridden + * method. It is null if the overridden method is + * abstract or declared in the interface. + * @param args an array of objects containing the values of + * the arguments passed in the method invocation + * on the proxy instance. If a parameter type is + * a primitive type, the type of the array element + * is a wrapper class. + * @return the resulting value of the method invocation. + * + * @throws Throwable if the method invocation fails. + */ + Object invoke(Object self, Method thisMethod, Method proceed, + Object[] args) throws Throwable; +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/Proxy.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/Proxy.java new file mode 100644 index 0000000..3907742 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/Proxy.java @@ -0,0 +1,33 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +/** + * The interface implemented by proxy classes. + * This interface only provides a setter method. + * To obtain a handler, call {@link ProxyFactory#getHandler(Proxy)}. + * + * @see ProxyFactory + * @since 3.16 + */ +public interface Proxy { + /** + * Sets a handler. It can be used for changing handlers + * during runtime. + */ + void setHandler(MethodHandler mi); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java new file mode 100644 index 0000000..270fc56 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java @@ -0,0 +1,1446 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.security.ProtectionDomain; +import java.util.*; +import java.lang.ref.WeakReference; + +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.NotFoundException; +import com.wenshuo.agent.javassist.bytecode.*; + +/* + * This class is implemented only with the lower-level API of Javassist. + * This design decision is for maximizing performance. + */ + +/** + * Factory of dynamic proxy classes. + * + *

This factory generates a class that extends the given super class and implements + * the given interfaces. The calls of the methods inherited from the super class are + * forwarded and then invoke() is called on the method handler + * associated with instances of the generated class. The calls of the methods from + * the interfaces are also forwarded to the method handler. + * + *

For example, if the following code is executed, + * + *

+ * ProxyFactory f = new ProxyFactory();
+ * f.setSuperclass(Foo.class);
+ * f.setFilter(new MethodFilter() {
+ *     public boolean isHandled(Method m) {
+ *         // ignore finalize()
+ *         return !m.getName().equals("finalize");
+ *     }
+ * });
+ * Class c = f.createClass();
+ * MethodHandler mi = new MethodHandler() {
+ *     public Object invoke(Object self, Method m, Method proceed,
+ *                          Object[] args) throws Throwable {
+ *         System.out.println("Name: " + m.getName());
+ *         return proceed.invoke(self, args);  // execute the original method.
+ *     }
+ * };
+ * Foo foo = (Foo)c.newInstance();
+ * ((Proxy)foo).setHandler(mi);
+ * 
+ * + *

Here, Method is java.lang.reflect.Method.

+ * + *

Then, the following method call will be forwarded to MethodHandler + * mi and prints a message before executing the originally called method + * bar() in Foo. + * + *

+ * foo.bar();
+ * 
+ * + *

The last three lines of the code shown above can be replaced with a call to + * the helper method create, which generates a proxy class, instantiates + * it, and sets the method handler of the instance: + * + *

+ *     :
+ * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi);
+ * 
+ * + *

To change the method handler during runtime, + * execute the following code: + * + *

+ * MethodHandler mi = ... ;    // alternative handler
+ * ((Proxy)foo).setHandler(mi);
+ * 
+ * + *

If setHandler is never called for a proxy instance then it will + * employ the default handler which proceeds by invoking the original method. + * The behaviour of the default handler is identical to the following + * handler: + * + *

+ * class EmptyHandler implements MethodHandler {
+ *     public Object invoke(Object self, Method m,
+ *                          Method proceed, Object[] args) throws Exception {
+ *         return proceed.invoke(self, args);
+ *     }
+ * }
+ * 
+ * + *

A proxy factory caches and reuses proxy classes by default. It is possible to reset + * this default globally by setting static field {@link ProxyFactory#useCache} to false. + * Caching may also be configured for a specific factory by calling instance method + * {@link ProxyFactory#setUseCache(boolean)}. It is strongly recommended that new clients + * of class ProxyFactory enable caching. Failure to do so may lead to exhaustion of + * the heap memory area used to store classes. + * + *

Caching is automatically disabled for any given proxy factory if deprecated instance + * method {@link ProxyFactory#setHandler(MethodHandler)} is called. This method was + * used to specify a default handler which newly created proxy classes should install + * when they create their instances. It is only retained to provide backward compatibility + * with previous releases of javassist. Unfortunately,this legacy behaviour makes caching + * and reuse of proxy classes impossible. The current programming model expects javassist + * clients to set the handler of a proxy instance explicitly by calling method + * {@link Proxy#setHandler(MethodHandler)} as shown in the sample code above. New + * clients are strongly recommended to use this model rather than calling + * {@link ProxyFactory#setHandler(MethodHandler)}. + * + *

A proxy object generated by ProxyFactory is serializable + * if its super class or any of its interfaces implement java.io.Serializable. + * However, a serialized proxy object may not be compatible with future releases. + * The serialization support should be used for short-term storage or RMI. + * + *

For compatibility with older releases serialization of proxy objects is implemented by + * adding a writeReplace method to the proxy class. This allows a proxy to be serialized + * to a conventional {@link java.io.ObjectOutputStream} and deserialized from a corresponding + * {@link java.io.ObjectInputStream}. However this method suffers from several problems, the most + * notable one being that it fails to serialize state inherited from the proxy's superclass. + *

+ * An alternative method of serializing proxy objects is available which fixes these problems. It + * requires inhibiting generation of the writeReplace method and instead using instances of + * {@link javassist.util.proxy.ProxyObjectOutputStream} and {@link javassist.util.proxy.ProxyObjectInputStream} + * (which are subclasses of {@link java.io.ObjectOutputStream} and {@link java.io.ObjectInputStream}) + * to serialize and deserialize, respectively, the proxy. These streams recognise javassist proxies and ensure + * that they are serialized and deserialized without the need for the proxy class to implement special methods + * such as writeReplace. Generation of the writeReplace method can be disabled globally by setting static field + * {@link ProxyFactory#useWriteReplace} to false. Alternatively, it may be + * configured per factory by calling instance method {@link ProxyFactory#setUseWriteReplace(boolean)}. + * + * @see MethodHandler + * @since 3.1 + * @author Muga Nishizawa + * @author Shigeru Chiba + * @author Andrew Dinn + */ +public class ProxyFactory { + private Class superClass; + private Class[] interfaces; + private MethodFilter methodFilter; + private MethodHandler handler; // retained for legacy usage + private List signatureMethods; + private boolean hasGetHandler; + private byte[] signature; + private String classname; + private String basename; + private String superName; + private Class thisClass; + /** + * per factory setting initialised from current setting for useCache but able to be reset before each create call + */ + private boolean factoryUseCache; + /** + * per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call + */ + private boolean factoryWriteReplace; + + + /** + * If the value of this variable is not null, the class file of + * the generated proxy class is written under the directory specified + * by this variable. For example, if the value is + * ".", then the class file is written under the current + * directory. This method is for debugging. + * + *

The default value is null. + */ + public String writeDirectory; + + private static final Class OBJECT_TYPE = Object.class; + + private static final String HOLDER = "_methods_"; + private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;"; + private static final String FILTER_SIGNATURE_FIELD = "_filter_signature"; + private static final String FILTER_SIGNATURE_TYPE = "[B"; + private static final String HANDLER = "handler"; + private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport"; + private static final String DEFAULT_INTERCEPTOR = "default_interceptor"; + private static final String HANDLER_TYPE + = 'L' + MethodHandler.class.getName().replace('.', '/') + ';'; + private static final String HANDLER_SETTER = "setHandler"; + private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V"; + + private static final String HANDLER_GETTER = "getHandler"; + private static final String HANDLER_GETTER_TYPE = "()" + HANDLER_TYPE; + + private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID"; + private static final String SERIAL_VERSION_UID_TYPE = "J"; + private static final long SERIAL_VERSION_UID_VALUE = -1L; + + /** + * If true, a generated proxy class is cached and it will be reused + * when generating the proxy class with the same properties is requested. + * The default value is true. + * + * Note that this value merely specifies the initial setting employed by any newly created + * proxy factory. The factory setting may be overwritten by calling factory instance method + * {@link #setUseCache(boolean)} + * + * @since 3.4 + */ + public static volatile boolean useCache = true; + + /** + * If true, a generated proxy class will implement method writeReplace enabling + * serialization of its proxies to a conventional ObjectOutputStream. this (default) + * setting retains the old javassist behaviour which has the advantage that it + * retains compatibility with older releases and requires no extra work on the part + * of the client performing the serialization. However, it has the disadvantage that + * state inherited from the superclasses of the proxy is lost during serialization. + * if false then serialization/deserialization of the proxy instances will preserve + * all fields. However, serialization must be performed via a {@link ProxyObjectOutputStream} + * and deserialization must be via {@link ProxyObjectInputStream}. Any attempt to serialize + * proxies whose class was created with useWriteReplace set to false via a normal + * {@link java.io.ObjectOutputStream} will fail. + * + * Note that this value merely specifies the initial setting employed by any newly created + * proxy factory. The factory setting may be overwritten by calling factory instance method + * {@link #setUseWriteReplace(boolean)} + * + * @since 3.4 + */ + public static volatile boolean useWriteReplace = true; + + /* + * methods allowing individual factory settings for factoryUseCache and factoryWriteReplace to be reset + */ + + /** + * test whether this factory uses the proxy cache + * @return true if this factory uses the proxy cache otherwise false + */ + public boolean isUseCache() + { + return factoryUseCache; + } + + /** + * configure whether this factory should use the proxy cache + * @param useCache true if this factory should use the proxy cache and false if it should not use the cache + * @throws RuntimeException if a default interceptor has been set for the factory + */ + public void setUseCache(boolean useCache) + { + // we cannot allow caching to be used if the factory is configured to install a default interceptor + // field into generated classes + if (handler != null && useCache) { + throw new RuntimeException("caching cannot be enabled if the factory default interceptor has been set"); + } + factoryUseCache = useCache; + } + + /** + * test whether this factory installs a writeReplace method in created classes + * @return true if this factory installs a writeReplace method in created classes otherwise false + */ + public boolean isUseWriteReplace() + { + return factoryWriteReplace; + } + + /** + * configure whether this factory should add a writeReplace method to created classes + * @param useWriteReplace true if this factory should add a writeReplace method to created classes and false if it + * should not add a writeReplace method + */ + public void setUseWriteReplace(boolean useWriteReplace) + { + factoryWriteReplace = useWriteReplace; + } + + private static WeakHashMap proxyCache = new WeakHashMap(); + + /** + * determine if a class is a javassist proxy class + * @param cl + * @return true if the class is a javassist proxy class otherwise false + */ + public static boolean isProxyClass(Class cl) + { + // all proxies implement Proxy or ProxyObject. nothing else should. + return (Proxy.class.isAssignableFrom(cl)); + } + + /** + * used to store details of a specific proxy class in the second tier of the proxy cache. this entry + * will be located in a hashmap keyed by the unique identifying name of the proxy class. the hashmap is + * located in a weak hashmap keyed by the classloader common to all proxy classes in the second tier map. + */ + static class ProxyDetails { + /** + * the unique signature of any method filter whose behaviour will be met by this class. each bit in + * the byte array is set if the filter redirects the corresponding super or interface method and clear + * if it does not redirect it. + */ + byte[] signature; + /** + * a hexadecimal string representation of the signature bit sequence. this string also forms part + * of the proxy class name. + */ + WeakReference proxyClass; + /** + * a flag which is true this class employs writeReplace to perform serialization of its instances + * and false if serialization must employ of a ProxyObjectOutputStream and ProxyObjectInputStream + */ + boolean isUseWriteReplace; + + ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace) + { + this.signature = signature; + this.proxyClass = new WeakReference(proxyClass); + this.isUseWriteReplace = isUseWriteReplace; + } + } + + /** + * Constructs a factory of proxy class. + */ + public ProxyFactory() { + superClass = null; + interfaces = null; + methodFilter = null; + handler = null; + signature = null; + signatureMethods = null; + hasGetHandler = false; + thisClass = null; + writeDirectory = null; + factoryUseCache = useCache; + factoryWriteReplace = useWriteReplace; + } + + /** + * Sets the super class of a proxy class. + */ + public void setSuperclass(Class clazz) { + superClass = clazz; + // force recompute of signature + signature = null; + } + + /** + * Obtains the super class set by setSuperclass(). + * + * @since 3.4 + */ + public Class getSuperclass() { return superClass; } + + /** + * Sets the interfaces of a proxy class. + */ + public void setInterfaces(Class[] ifs) { + interfaces = ifs; + // force recompute of signature + signature = null; + } + + /** + * Obtains the interfaces set by setInterfaces. + * + * @since 3.4 + */ + public Class[] getInterfaces() { return interfaces; } + + /** + * Sets a filter that selects the methods that will be controlled by a handler. + */ + public void setFilter(MethodFilter mf) { + methodFilter = mf; + // force recompute of signature + signature = null; + } + + /** + * Generates a proxy class using the current filter. + */ + public Class createClass() { + if (signature == null) { + computeSignature(methodFilter); + } + return createClass1(); + } + + /** + * Generates a proxy class using the supplied filter. + */ + public Class createClass(MethodFilter filter) { + computeSignature(filter); + return createClass1(); + } + + /** + * Generates a proxy class with a specific signature. + * access is package local so ProxyObjectInputStream can use this + * @param signature + * @return + */ + Class createClass(byte[] signature) + { + installSignature(signature); + return createClass1(); + } + + private Class createClass1() { + if (thisClass == null) { + ClassLoader cl = getClassLoader(); + synchronized (proxyCache) { + if (factoryUseCache) + createClass2(cl); + else + createClass3(cl); + } + } + + // don't retain any unwanted references + Class result = thisClass; + thisClass = null; + + return result; + } + + private static char[] hexDigits = + { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + public String getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace) + { + StringBuffer sbuf = new StringBuffer(); + if (superClass != null){ + sbuf.append(superClass.getName()); + } + sbuf.append(":"); + for (int i = 0; i < interfaces.length; i++) { + sbuf.append(interfaces[i].getName()); + sbuf.append(":"); + } + for (int i = 0; i < signature.length; i++) { + byte b = signature[i]; + int lo = b & 0xf; + int hi = (b >> 4) & 0xf; + sbuf.append(hexDigits[lo]); + sbuf.append(hexDigits[hi]); + } + if (useWriteReplace) { + sbuf.append(":w"); + } + + return sbuf.toString(); + } + + private void createClass2(ClassLoader cl) { + String key = getKey(superClass, interfaces, signature, factoryWriteReplace); + /* + * Excessive concurrency causes a large memory footprint and slows the + * execution speed down (with JDK 1.5). Thus, we use a jumbo lock for + * reducing concrrency. + */ + // synchronized (proxyCache) { + HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl); + ProxyDetails details; + if (cacheForTheLoader == null) { + cacheForTheLoader = new HashMap(); + proxyCache.put(cl, cacheForTheLoader); + } + details = (ProxyDetails)cacheForTheLoader.get(key); + if (details != null) { + WeakReference reference = details.proxyClass; + thisClass = (Class)reference.get(); + if (thisClass != null) { + return; + } + } + createClass3(cl); + details = new ProxyDetails(signature, thisClass, factoryWriteReplace); + cacheForTheLoader.put(key, details); + // } + } + + private void createClass3(ClassLoader cl) { + // we need a new class so we need a new class name + allocateClassName(); + + try { + ClassFile cf = make(); + if (writeDirectory != null) + FactoryHelper.writeFile(cf, writeDirectory); + + thisClass = FactoryHelper.toClass(cf, cl, getDomain()); + setField(FILTER_SIGNATURE_FIELD, signature); + // legacy behaviour : we only set the default interceptor static field if we are not using the cache + if (!factoryUseCache) { + setField(DEFAULT_INTERCEPTOR, handler); + } + } + catch (CannotCompileException e) { + throw new RuntimeException(e.getMessage(), e); + } + + } + + private void setField(String fieldName, Object value) { + if (thisClass != null && value != null) + try { + Field f = thisClass.getField(fieldName); + SecurityActions.setAccessible(f, true); + f.set(null, value); + SecurityActions.setAccessible(f, false); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + static byte[] getFilterSignature(Class clazz) { + return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD); + } + + private static Object getField(Class clazz, String fieldName) { + try { + Field f = clazz.getField(fieldName); + f.setAccessible(true); + Object value = f.get(null); + f.setAccessible(false); + return value; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Obtains the method handler of the given proxy object. + * + * @param p a proxy object. + * @return the method handler. + * @since 3.16 + */ + public static MethodHandler getHandler(Proxy p) { + try { + Field f = p.getClass().getDeclaredField(HANDLER); + f.setAccessible(true); + Object value = f.get(p); + f.setAccessible(false); + return (MethodHandler)value; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * A provider of class loaders. + * + * @see #classLoaderProvider + * @since 3.4 + */ + public static interface ClassLoaderProvider { + /** + * Returns a class loader. + * + * @param pf a proxy factory that is going to obtain a class loader. + */ + public ClassLoader get(ProxyFactory pf); + } + + /** + * A provider used by createClass() for obtaining + * a class loader. + * get() on this ClassLoaderProvider object + * is called to obtain a class loader. + * + *

The value of this field can be updated for changing the default + * implementation. + * + *

Example: + *

+     * ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
+     *     public ClassLoader get(ProxyFactory pf) {
+     *         return Thread.currentThread().getContextClassLoader();
+     *     }
+     * };
+     * 
+ * + * @since 3.4 + */ + public static ClassLoaderProvider classLoaderProvider + = new ClassLoaderProvider() { + public ClassLoader get(ProxyFactory pf) { + return pf.getClassLoader0(); + } + }; + + protected ClassLoader getClassLoader() { + return classLoaderProvider.get(this); + } + + protected ClassLoader getClassLoader0() { + ClassLoader loader = null; + if (superClass != null && !superClass.getName().equals("java.lang.Object")) + loader = superClass.getClassLoader(); + else if (interfaces != null && interfaces.length > 0) + loader = interfaces[0].getClassLoader(); + + if (loader == null) { + loader = getClass().getClassLoader(); + // In case javassist is in the endorsed dir + if (loader == null) { + loader = Thread.currentThread().getContextClassLoader(); + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + } + } + + return loader; + } + + protected ProtectionDomain getDomain() { + Class clazz; + if (superClass != null && !superClass.getName().equals("java.lang.Object")) + clazz = superClass; + else if (interfaces != null && interfaces.length > 0) + clazz = interfaces[0]; + else + clazz = this.getClass(); + + return clazz.getProtectionDomain(); + } + + /** + * Creates a proxy class and returns an instance of that class. + * + * @param paramTypes parameter types for a constructor. + * @param args arguments passed to a constructor. + * @param mh the method handler for the proxy class. + * @since 3.4 + */ + public Object create(Class[] paramTypes, Object[] args, MethodHandler mh) + throws NoSuchMethodException, IllegalArgumentException, + InstantiationException, IllegalAccessException, InvocationTargetException + { + Object obj = create(paramTypes, args); + ((Proxy)obj).setHandler(mh); + return obj; + } + + /** + * Creates a proxy class and returns an instance of that class. + * + * @param paramTypes parameter types for a constructor. + * @param args arguments passed to a constructor. + */ + public Object create(Class[] paramTypes, Object[] args) + throws NoSuchMethodException, IllegalArgumentException, + InstantiationException, IllegalAccessException, InvocationTargetException + { + Class c = createClass(); + Constructor cons = c.getConstructor(paramTypes); + return cons.newInstance(args); + } + + /** + * Sets the default invocation handler. This invocation handler is shared + * among all the instances of a proxy class unless another is explicitly + * specified. + * @deprecated since 3.12 + * use of this method is incompatible with proxy class caching. + * instead clients should call method {@link Proxy#setHandler(MethodHandler)} to set the handler + * for each newly created proxy instance. + * calling this method will automatically disable caching of classes created by the proxy factory. + */ + public void setHandler(MethodHandler mi) { + // if we were using the cache and the handler is non-null then we must stop caching + if (factoryUseCache && mi != null) { + factoryUseCache = false; + // clear any currently held class so we don't try to reuse it or set its handler field + thisClass = null; + } + handler = mi; + // this retains the behaviour of the old code which resets any class we were holding on to + // this is probably not what is wanted + setField(DEFAULT_INTERCEPTOR, handler); + } + + /** + * A unique class name generator. + */ + public static interface UniqueName { + /** + * Returns a unique class name. + * + * @param classname the super class name of the proxy class. + */ + String get(String classname); + } + + /** + * A unique class name generator. + * Replacing this generator changes the algorithm to generate a + * unique name. The get method does not have to be + * a synchronized method since the access to this field + * is mutually exclusive and thus thread safe. + */ + public static UniqueName nameGenerator = new UniqueName() { + private final String sep = "_$$_jvst" + Integer.toHexString(this.hashCode() & 0xfff) + "_"; + private int counter = 0; + + public String get(String classname) { + return classname + sep + Integer.toHexString(counter++); + } + }; + + private static String makeProxyName(String classname) { + synchronized (nameGenerator) { + return nameGenerator.get(classname); + } + } + + private ClassFile make() throws CannotCompileException { + ClassFile cf = new ClassFile(false, classname, superName); + cf.setAccessFlags(AccessFlag.PUBLIC); + setInterfaces(cf, interfaces, hasGetHandler ? Proxy.class : ProxyObject.class); + ConstPool pool = cf.getConstPool(); + + // legacy: we only add the static field for the default interceptor if caching is disabled + if (!factoryUseCache) { + FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE); + finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); + cf.addField(finfo); + } + + // handler is per instance + FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE); + finfo2.setAccessFlags(AccessFlag.PRIVATE); + cf.addField(finfo2); + + // filter signature is per class + FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE); + finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); + cf.addField(finfo3); + + // the proxy class serial uid must always be a fixed value + FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE); + finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL); + cf.addField(finfo4); + + // HashMap allMethods = getMethods(superClass, interfaces); + // int size = allMethods.size(); + makeConstructors(classname, cf, pool, classname); + + ArrayList forwarders = new ArrayList(); + int s = overrideMethods(cf, pool, classname, forwarders); + addClassInitializer(cf, pool, classname, s, forwarders); + addSetter(classname, cf, pool); + if (!hasGetHandler) + addGetter(classname, cf, pool); + + if (factoryWriteReplace) { + try { + cf.addMethod(makeWriteReplace(pool)); + } + catch (DuplicateMemberException e) { + // writeReplace() is already declared in the super class/interfaces. + } + } + + thisClass = null; + return cf; + } + + private void checkClassAndSuperName() { + if (interfaces == null) + interfaces = new Class[0]; + + if (superClass == null) { + superClass = OBJECT_TYPE; + superName = superClass.getName(); + basename = interfaces.length == 0 ? superName + : interfaces[0].getName(); + } else { + superName = superClass.getName(); + basename = superName; + } + + if (Modifier.isFinal(superClass.getModifiers())) + throw new RuntimeException(superName + " is final"); + + if (basename.startsWith("java.")) + basename = "org.javassist.tmp." + basename; + } + + private void allocateClassName() { + classname = makeProxyName(basename); + } + + private static Comparator sorter = new Comparator() { + + public int compare(Object o1, Object o2) { + Map.Entry e1 = (Map.Entry)o1; + Map.Entry e2 = (Map.Entry)o2; + String key1 = (String)e1.getKey(); + String key2 = (String)e2.getKey(); + return key1.compareTo(key2); + } + }; + + private void makeSortedMethodList() { + checkClassAndSuperName(); + + hasGetHandler = false; // getMethods() may set this to true. + HashMap allMethods = getMethods(superClass, interfaces); + signatureMethods = new ArrayList(allMethods.entrySet()); + Collections.sort(signatureMethods, sorter); + } + + private void computeSignature(MethodFilter filter) // throws CannotCompileException + { + makeSortedMethodList(); + + int l = signatureMethods.size(); + int maxBytes = ((l + 7) >> 3); + signature = new byte[maxBytes]; + for (int idx = 0; idx < l; idx++) + { + Map.Entry e = (Map.Entry)signatureMethods.get(idx); + Method m = (Method)e.getValue(); + int mod = m.getModifiers(); + if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod) + && isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) { + setBit(signature, idx); + } + } + } + + private void installSignature(byte[] signature) // throws CannotCompileException + { + makeSortedMethodList(); + + int l = signatureMethods.size(); + int maxBytes = ((l + 7) >> 3); + if (signature.length != maxBytes) { + throw new RuntimeException("invalid filter signature length for deserialized proxy class"); + } + + this.signature = signature; + } + + private boolean testBit(byte[] signature, int idx) { + int byteIdx = idx >> 3; + if (byteIdx > signature.length) { + return false; + } else { + int bitIdx = idx & 0x7; + int mask = 0x1 << bitIdx; + int sigByte = signature[byteIdx]; + return ((sigByte & mask) != 0); + } + } + + private void setBit(byte[] signature, int idx) { + int byteIdx = idx >> 3; + if (byteIdx < signature.length) { + int bitIdx = idx & 0x7; + int mask = 0x1 << bitIdx; + int sigByte = signature[byteIdx]; + signature[byteIdx] = (byte)(sigByte | mask); + } + } + + private static void setInterfaces(ClassFile cf, Class[] interfaces, Class proxyClass) { + String setterIntf = proxyClass.getName(); + String[] list; + if (interfaces == null || interfaces.length == 0) + list = new String[] { setterIntf }; + else { + list = new String[interfaces.length + 1]; + for (int i = 0; i < interfaces.length; i++) + list[i] = interfaces[i].getName(); + + list[interfaces.length] = setterIntf; + } + + cf.setInterfaces(list); + } + + private static void addClassInitializer(ClassFile cf, ConstPool cp, + String classname, int size, ArrayList forwarders) + throws CannotCompileException + { + FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE); + finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC); + cf.addField(finfo); + MethodInfo minfo = new MethodInfo(cp, "", "()V"); + minfo.setAccessFlags(AccessFlag.STATIC); + setThrows(minfo, cp, new Class[] { ClassNotFoundException.class }); + + Bytecode code = new Bytecode(cp, 0, 2); + code.addIconst(size * 2); + code.addAnewarray("java.lang.reflect.Method"); + final int varArray = 0; + code.addAstore(varArray); + + // forName() must be called here. Otherwise, the class might be + // invisible. + code.addLdc(classname); + code.addInvokestatic("java.lang.Class", + "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); + final int varClass = 1; + code.addAstore(varClass); + + Iterator it = forwarders.iterator(); + while (it.hasNext()) { + Find2MethodsArgs args = (Find2MethodsArgs)it.next(); + callFind2Methods(code, args.methodName, args.delegatorName, + args.origIndex, args.descriptor, varClass, varArray); + } + + code.addAload(varArray); + code.addPutstatic(classname, HOLDER, HOLDER_TYPE); + + code.addLconst(SERIAL_VERSION_UID_VALUE); + code.addPutstatic(classname, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE); + code.addOpcode(Bytecode.RETURN); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + } + + /** + * @param thisMethod might be null. + */ + private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod, + int index, String desc, int classVar, int arrayVar) { + String findClass = RuntimeSupport.class.getName(); + String findDesc + = "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/reflect/Method;)V"; + + code.addAload(classVar); + code.addLdc(superMethod); + if (thisMethod == null) + code.addOpcode(Opcode.ACONST_NULL); + else + code.addLdc(thisMethod); + + code.addIconst(index); + code.addLdc(desc); + code.addAload(arrayVar); + code.addInvokestatic(findClass, "find2Methods", findDesc); + } + + private static void addSetter(String classname, ClassFile cf, ConstPool cp) + throws CannotCompileException + { + MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER, + HANDLER_SETTER_TYPE); + minfo.setAccessFlags(AccessFlag.PUBLIC); + Bytecode code = new Bytecode(cp, 2, 2); + code.addAload(0); + code.addAload(1); + code.addPutfield(classname, HANDLER, HANDLER_TYPE); + code.addOpcode(Bytecode.RETURN); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + } + + private static void addGetter(String classname, ClassFile cf, ConstPool cp) + throws CannotCompileException + { + MethodInfo minfo = new MethodInfo(cp, HANDLER_GETTER, + HANDLER_GETTER_TYPE); + minfo.setAccessFlags(AccessFlag.PUBLIC); + Bytecode code = new Bytecode(cp, 1, 1); + code.addAload(0); + code.addGetfield(classname, HANDLER, HANDLER_TYPE); + code.addOpcode(Bytecode.ARETURN); + minfo.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(minfo); + } + + private int overrideMethods(ClassFile cf, ConstPool cp, String className, ArrayList forwarders) + throws CannotCompileException + { + String prefix = makeUniqueName("_d", signatureMethods); + Iterator it = signatureMethods.iterator(); + int index = 0; + while (it.hasNext()) { + Map.Entry e = (Map.Entry)it.next(); + String key = (String)e.getKey(); + Method meth = (Method)e.getValue(); + if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_5 || !isBridge(meth)) + if (testBit(signature, index)) { + override(className, meth, prefix, index, + keyToDesc(key, meth), cf, cp, forwarders); + } + + index++; + } + + return index; + } + + private static boolean isBridge(Method m) { + return m.isBridge(); + } + + private void override(String thisClassname, Method meth, String prefix, + int index, String desc, ClassFile cf, ConstPool cp, ArrayList forwarders) + throws CannotCompileException + { + Class declClass = meth.getDeclaringClass(); + String delegatorName = prefix + index + meth.getName(); + if (Modifier.isAbstract(meth.getModifiers())) + delegatorName = null; + else { + MethodInfo delegator + = makeDelegator(meth, desc, cp, declClass, delegatorName); + // delegator is not a bridge method. See Sec. 15.12.4.5 of JLS 3rd Ed. + delegator.setAccessFlags(delegator.getAccessFlags() & ~AccessFlag.BRIDGE); + cf.addMethod(delegator); + } + + MethodInfo forwarder + = makeForwarder(thisClassname, meth, desc, cp, declClass, + delegatorName, index, forwarders); + cf.addMethod(forwarder); + } + + private void makeConstructors(String thisClassName, ClassFile cf, + ConstPool cp, String classname) throws CannotCompileException + { + Constructor[] cons = SecurityActions.getDeclaredConstructors(superClass); + // legacy: if we are not caching then we need to initialise the default handler + boolean doHandlerInit = !factoryUseCache; + for (int i = 0; i < cons.length; i++) { + Constructor c = cons[i]; + int mod = c.getModifiers(); + if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod) + && isVisible(mod, basename, c)) { + MethodInfo m = makeConstructor(thisClassName, c, cp, superClass, doHandlerInit); + cf.addMethod(m); + } + } + } + + private static String makeUniqueName(String name, List sortedMethods) { + if (makeUniqueName0(name, sortedMethods.iterator())) + return name; + + for (int i = 100; i < 999; i++) { + String s = name + i; + if (makeUniqueName0(s, sortedMethods.iterator())) + return s; + } + + throw new RuntimeException("cannot make a unique method name"); + } + + private static boolean makeUniqueName0(String name, Iterator it) { + while (it.hasNext()) { + Map.Entry e = (Map.Entry)it.next(); + String key = (String)e.getKey(); + if (key.startsWith(name)) + return false; + } + + return true; + } + + /** + * Returns true if the method is visible from the package. + * + * @param mod the modifiers of the method. + */ + private static boolean isVisible(int mod, String from, Member meth) { + if ((mod & Modifier.PRIVATE) != 0) + return false; + else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) + return true; + else { + String p = getPackageName(from); + String q = getPackageName(meth.getDeclaringClass().getName()); + if (p == null) + return q == null; + else + return p.equals(q); + } + } + + private static String getPackageName(String name) { + int i = name.lastIndexOf('.'); + if (i < 0) + return null; + else + return name.substring(0, i); + } + + /* getMethods() may set hasGetHandler to true. + */ + private HashMap getMethods(Class superClass, Class[] interfaceTypes) { + HashMap hash = new HashMap(); + HashSet set = new HashSet(); + for (int i = 0; i < interfaceTypes.length; i++) + getMethods(hash, interfaceTypes[i], set); + + getMethods(hash, superClass, set); + return hash; + } + + private void getMethods(HashMap hash, Class clazz, Set visitedClasses) { + // This both speeds up scanning by avoiding duplicate interfaces and is needed to + // ensure that superinterfaces are always scanned before subinterfaces. + if (!visitedClasses.add(clazz)) + return; + + Class[] ifs = clazz.getInterfaces(); + for (int i = 0; i < ifs.length; i++) + getMethods(hash, ifs[i], visitedClasses); + + Class parent = clazz.getSuperclass(); + if (parent != null) + getMethods(hash, parent, visitedClasses); + + /* Java 5 or later allows covariant return types. + * It also allows contra-variant parameter types + * if a super class is a generics with concrete type arguments + * such as Foo. So the method-overriding rule is complex. + */ + Method[] methods = SecurityActions.getDeclaredMethods(clazz); + for (int i = 0; i < methods.length; i++) + if (!Modifier.isPrivate(methods[i].getModifiers())) { + Method m = methods[i]; + String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m); // see keyToDesc(). + if (key.startsWith(HANDLER_GETTER_KEY)) + hasGetHandler = true; + + // JIRA JASSIST-85 + // put the method to the cache, retrieve previous definition (if any) + Method oldMethod = (Method)hash.put(key, methods[i]); + + // check if visibility has been reduced + if (null != oldMethod && Modifier.isPublic(oldMethod.getModifiers()) + && !Modifier.isPublic(methods[i].getModifiers()) ) { + // we tried to overwrite a public definition with a non-public definition, + // use the old definition instead. + hash.put(key, oldMethod); + } + } + } + + private static final String HANDLER_GETTER_KEY + = HANDLER_GETTER + ":()"; + + private static String keyToDesc(String key, Method m) { + return key.substring(key.indexOf(':') + 1); + } + + private static MethodInfo makeConstructor(String thisClassName, Constructor cons, + ConstPool cp, Class superClass, boolean doHandlerInit) { + String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(), + Void.TYPE); + MethodInfo minfo = new MethodInfo(cp, "", desc); + minfo.setAccessFlags(Modifier.PUBLIC); // cons.getModifiers() & ~Modifier.NATIVE + setThrows(minfo, cp, cons.getExceptionTypes()); + Bytecode code = new Bytecode(cp, 0, 0); + + // legacy: if we are not using caching then we initialise the instance's handler + // from the class's static default interceptor and skip the next few instructions if + // it is non-null + if (doHandlerInit) { + code.addAload(0); + code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE); + code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE); + code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE); + code.addOpcode(Opcode.IFNONNULL); + code.addIndex(10); + } + // if caching is enabled then we don't have a handler to initialise so this else branch will install + // the handler located in the static field of class RuntimeSupport. + code.addAload(0); + code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR, HANDLER_TYPE); + code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE); + int pc = code.currentPc(); + + code.addAload(0); + int s = addLoadParameters(code, cons.getParameterTypes(), 1); + code.addInvokespecial(superClass.getName(), "", desc); + code.addOpcode(Opcode.RETURN); + code.setMaxLocals(s + 1); + CodeAttribute ca = code.toCodeAttribute(); + minfo.setCodeAttribute(ca); + + StackMapTable.Writer writer = new StackMapTable.Writer(32); + writer.sameFrame(pc); + ca.setAttribute(writer.toStackMapTable(cp)); + return minfo; + } + + private static MethodInfo makeDelegator(Method meth, String desc, + ConstPool cp, Class declClass, String delegatorName) { + MethodInfo delegator = new MethodInfo(cp, delegatorName, desc); + delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC + | (meth.getModifiers() & ~(Modifier.PRIVATE + | Modifier.PROTECTED + | Modifier.ABSTRACT + | Modifier.NATIVE + | Modifier.SYNCHRONIZED))); + setThrows(delegator, cp, meth); + Bytecode code = new Bytecode(cp, 0, 0); + code.addAload(0); + int s = addLoadParameters(code, meth.getParameterTypes(), 1); + code.addInvokespecial(declClass.getName(), meth.getName(), desc); + addReturn(code, meth.getReturnType()); + code.setMaxLocals(++s); + delegator.setCodeAttribute(code.toCodeAttribute()); + return delegator; + } + + /** + * @param delegatorName null if the original method is abstract. + */ + private static MethodInfo makeForwarder(String thisClassName, + Method meth, String desc, ConstPool cp, + Class declClass, String delegatorName, int index, + ArrayList forwarders) { + MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc); + forwarder.setAccessFlags(Modifier.FINAL + | (meth.getModifiers() & ~(Modifier.ABSTRACT + | Modifier.NATIVE + | Modifier.SYNCHRONIZED))); + setThrows(forwarder, cp, meth); + int args = Descriptor.paramSize(desc); + Bytecode code = new Bytecode(cp, 0, args + 2); + /* + * static { + * methods[index * 2] + * = RuntimeSupport.findSuperMethod(this, , ); + * methods[index * 2 + 1] + * = RuntimeSupport.findMethod(this, , ); + * or = null // the original method is abstract. + * } + * : + * return ($r)handler.invoke(this, methods[index * 2], + * methods[index * 2 + 1], $args); + */ + int origIndex = index * 2; + int delIndex = index * 2 + 1; + int arrayVar = args + 1; + code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE); + code.addAstore(arrayVar); + + forwarders.add(new Find2MethodsArgs(meth.getName(), delegatorName, desc, origIndex)); + + code.addAload(0); + code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE); + code.addAload(0); + + code.addAload(arrayVar); + code.addIconst(origIndex); + code.addOpcode(Opcode.AALOAD); + + code.addAload(arrayVar); + code.addIconst(delIndex); + code.addOpcode(Opcode.AALOAD); + + makeParameterList(code, meth.getParameterTypes()); + code.addInvokeinterface(MethodHandler.class.getName(), "invoke", + "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", + 5); + Class retType = meth.getReturnType(); + addUnwrapper(code, retType); + addReturn(code, retType); + + CodeAttribute ca = code.toCodeAttribute(); + forwarder.setCodeAttribute(ca); + return forwarder; + } + + static class Find2MethodsArgs { + String methodName, delegatorName, descriptor; + int origIndex; + + Find2MethodsArgs(String mname, String dname, String desc, int index) { + methodName = mname; + delegatorName = dname; + descriptor = desc; + origIndex = index; + } + } + + private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) { + Class[] exceptions = orig.getExceptionTypes(); + setThrows(minfo, cp, exceptions); + } + + private static void setThrows(MethodInfo minfo, ConstPool cp, + Class[] exceptions) { + if (exceptions.length == 0) + return; + + String[] list = new String[exceptions.length]; + for (int i = 0; i < exceptions.length; i++) + list[i] = exceptions[i].getName(); + + ExceptionsAttribute ea = new ExceptionsAttribute(cp); + ea.setExceptions(list); + minfo.setExceptionsAttribute(ea); + } + + private static int addLoadParameters(Bytecode code, Class[] params, + int offset) { + int stacksize = 0; + int n = params.length; + for (int i = 0; i < n; ++i) + stacksize += addLoad(code, stacksize + offset, params[i]); + + return stacksize; + } + + private static int addLoad(Bytecode code, int n, Class type) { + if (type.isPrimitive()) { + if (type == Long.TYPE) { + code.addLload(n); + return 2; + } + else if (type == Float.TYPE) + code.addFload(n); + else if (type == Double.TYPE) { + code.addDload(n); + return 2; + } + else + code.addIload(n); + } + else + code.addAload(n); + + return 1; + } + + private static int addReturn(Bytecode code, Class type) { + if (type.isPrimitive()) { + if (type == Long.TYPE) { + code.addOpcode(Opcode.LRETURN); + return 2; + } + else if (type == Float.TYPE) + code.addOpcode(Opcode.FRETURN); + else if (type == Double.TYPE) { + code.addOpcode(Opcode.DRETURN); + return 2; + } + else if (type == Void.TYPE) { + code.addOpcode(Opcode.RETURN); + return 0; + } + else + code.addOpcode(Opcode.IRETURN); + } + else + code.addOpcode(Opcode.ARETURN); + + return 1; + } + + private static void makeParameterList(Bytecode code, Class[] params) { + int regno = 1; + int n = params.length; + code.addIconst(n); + code.addAnewarray("java/lang/Object"); + for (int i = 0; i < n; i++) { + code.addOpcode(Opcode.DUP); + code.addIconst(i); + Class type = params[i]; + if (type.isPrimitive()) + regno = makeWrapper(code, type, regno); + else { + code.addAload(regno); + regno++; + } + + code.addOpcode(Opcode.AASTORE); + } + } + + private static int makeWrapper(Bytecode code, Class type, int regno) { + int index = FactoryHelper.typeIndex(type); + String wrapper = FactoryHelper.wrapperTypes[index]; + code.addNew(wrapper); + code.addOpcode(Opcode.DUP); + addLoad(code, regno, type); + code.addInvokespecial(wrapper, "", + FactoryHelper.wrapperDesc[index]); + return regno + FactoryHelper.dataSize[index]; + } + + private static void addUnwrapper(Bytecode code, Class type) { + if (type.isPrimitive()) { + if (type == Void.TYPE) + code.addOpcode(Opcode.POP); + else { + int index = FactoryHelper.typeIndex(type); + String wrapper = FactoryHelper.wrapperTypes[index]; + code.addCheckcast(wrapper); + code.addInvokevirtual(wrapper, + FactoryHelper.unwarpMethods[index], + FactoryHelper.unwrapDesc[index]); + } + } + else + code.addCheckcast(type.getName()); + } + + private static MethodInfo makeWriteReplace(ConstPool cp) { + MethodInfo minfo = new MethodInfo(cp, "writeReplace", "()Ljava/lang/Object;"); + String[] list = new String[1]; + list[0] = "java.io.ObjectStreamException"; + ExceptionsAttribute ea = new ExceptionsAttribute(cp); + ea.setExceptions(list); + minfo.setExceptionsAttribute(ea); + Bytecode code = new Bytecode(cp, 0, 1); + code.addAload(0); + code.addInvokestatic("javassist.util.proxy.RuntimeSupport", + "makeSerializedProxy", + "(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;"); + code.addOpcode(Opcode.ARETURN); + minfo.setCodeAttribute(code.toCodeAttribute()); + return minfo; + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java new file mode 100644 index 0000000..59f8f8b --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java @@ -0,0 +1,44 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +/** + * The interface implemented by proxy classes. + * This interface is available only if the super class of the proxy object + * does not have a getHandler() method. If the super class + * has getHandler, then Proxy interface is + * available. + * + * @see ProxyFactory + * @see Proxy + */ +public interface ProxyObject extends Proxy { + /** + * Sets a handler. It can be used for changing handlers + * during runtime. + */ + void setHandler(MethodHandler mi); + + /** + * Get the handler. + * This can be used to access the underlying MethodHandler + * or to serialize it properly. + * + * @see ProxyFactory#getHandler(Proxy) + */ + MethodHandler getHandler(); +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java new file mode 100644 index 0000000..ee81e88 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java @@ -0,0 +1,100 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; + +/** + * An input stream class which knows how to deserialize proxies created via {@link ProxyFactory} and + * serializedo via a {@link ProxyObjectOutputStream}. It must be used when deserialising proxies created + * from a proxy factory configured with {@link ProxyFactory#useWriteReplace} set to false. + * + * @author Andrew Dinn + */ +public class ProxyObjectInputStream extends ObjectInputStream +{ + /** + * create an input stream which can be used to deserialize an object graph which includes proxies created + * using class ProxyFactory. the classloader used to resolve proxy superclass and interface names + * read from the input stream will default to the current thread's context class loader or the system + * classloader if the context class loader is null. + * @param in + * @throws java.io.StreamCorruptedException whenever ObjectInputStream would also do so + * @throws IOException whenever ObjectInputStream would also do so + * @throws SecurityException whenever ObjectInputStream would also do so + * @throws NullPointerException if in is null + */ + public ProxyObjectInputStream(InputStream in) throws IOException + { + super(in); + loader = Thread.currentThread().getContextClassLoader(); + if (loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + } + + /** + * Reset the loader to be + * @param loader + */ + public void setClassLoader(ClassLoader loader) + { + if (loader != null) { + this.loader = loader; + } else { + loader = ClassLoader.getSystemClassLoader(); + } + } + + protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { + boolean isProxy = readBoolean(); + if (isProxy) { + String name = (String)readObject(); + Class superClass = loader.loadClass(name); + int length = readInt(); + Class[] interfaces = new Class[length]; + for (int i = 0; i < length; i++) { + name = (String)readObject(); + interfaces[i] = loader.loadClass(name); + } + length = readInt(); + byte[] signature = new byte[length]; + read(signature); + ProxyFactory factory = new ProxyFactory(); + // we must always use the cache and never use writeReplace when using + // ProxyObjectOutputStream and ProxyObjectInputStream + factory.setUseCache(true); + factory.setUseWriteReplace(false); + factory.setSuperclass(superClass); + factory.setInterfaces(interfaces); + Class proxyClass = factory.createClass(signature); + return ObjectStreamClass.lookup(proxyClass); + } else { + return super.readClassDescriptor(); + } + } + + /** + * the loader to use to resolve classes for proxy superclass and interface names read + * from the stream. defaults to the context class loader of the thread which creates + * the input stream or the system class loader if the context class loader is null. + */ + private ClassLoader loader; +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java new file mode 100644 index 0000000..84ac6cb --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java @@ -0,0 +1,72 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; + +/** + * An input stream class which knows how to serialize proxies created via {@link ProxyFactory}. It must + * be used when serialising proxies created from a proxy factory configured with + * {@link ProxyFactory#useWriteReplace} set to false. Subsequent deserialization of the serialized data + * must employ a {@link ProxyObjectInputStream} + * + * @author Andrew Dinn + */ +public class ProxyObjectOutputStream extends ObjectOutputStream +{ + /** + * create an output stream which can be used to serialize an object graph which includes proxies created + * using class ProxyFactory + * @param out + * @throws IOException whenever ObjectOutputStream would also do so + * @throws SecurityException whenever ObjectOutputStream would also do so + * @throws NullPointerException if out is null + */ + public ProxyObjectOutputStream(OutputStream out) throws IOException + { + super(out); + } + + protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { + Class cl = desc.forClass(); + if (ProxyFactory.isProxyClass(cl)) { + writeBoolean(true); + Class superClass = cl.getSuperclass(); + Class[] interfaces = cl.getInterfaces(); + byte[] signature = ProxyFactory.getFilterSignature(cl); + String name = superClass.getName(); + writeObject(name); + // we don't write the marker interface ProxyObject + writeInt(interfaces.length - 1); + for (int i = 0; i < interfaces.length; i++) { + Class interfaze = interfaces[i]; + if (interfaze != ProxyObject.class && interfaze != Proxy.class) { + name = interfaces[i].getName(); + writeObject(name); + } + } + writeInt(signature.length); + write(signature); + } else { + writeBoolean(false); + super.writeClassDescriptor(desc); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java new file mode 100644 index 0000000..9f4bbf5 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java @@ -0,0 +1,271 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +import java.lang.reflect.Method; +import java.io.Serializable; + +/** + * Runtime support routines that the classes generated by ProxyFactory use. + * + * @see ProxyFactory + */ +public class RuntimeSupport { + /** + * A method handler that only executes a method. + */ + public static MethodHandler default_interceptor = new DefaultMethodHandler(); + + static class DefaultMethodHandler implements MethodHandler, Serializable { + public Object invoke(Object self, Method m, + Method proceed, Object[] args) + throws Exception + { + return proceed.invoke(self, args); + } + }; + + /** + * Finds two methods specified by the parameters and stores them + * into the given array. + * + * @throws RuntimeException if the methods are not found. + * @see javassist.util.proxy.ProxyFactory + */ + public static void find2Methods(Class clazz, String superMethod, + String thisMethod, int index, + String desc, java.lang.reflect.Method[] methods) + { + methods[index + 1] = thisMethod == null ? null + : findMethod(clazz, thisMethod, desc); + methods[index] = findSuperClassMethod(clazz, superMethod, desc); + } + + /** + * Finds two methods specified by the parameters and stores them + * into the given array. + * + *

Added back for JBoss Seam. See JASSIST-206.

+ * + * @throws RuntimeException if the methods are not found. + * @see javassist.util.proxy.ProxyFactory + * @deprecated replaced by {@link #find2Methods(Class, String, String, int, String, Method[])} + */ + public static void find2Methods(Object self, String superMethod, + String thisMethod, int index, + String desc, java.lang.reflect.Method[] methods) + { + methods[index + 1] = thisMethod == null ? null + : findMethod(self, thisMethod, desc); + methods[index] = findSuperMethod(self, superMethod, desc); + } + + /** + * Finds a method with the given name and descriptor. + * It searches only the class of self. + * + *

Added back for JBoss Seam. See JASSIST-206.

+ * + * @throws RuntimeException if the method is not found. + * @deprecated replaced by {@link #findMethod(Class, String, String)} + */ + public static Method findMethod(Object self, String name, String desc) { + Method m = findMethod2(self.getClass(), name, desc); + if (m == null) + error(self.getClass(), name, desc); + + return m; + } + + /** + * Finds a method with the given name and descriptor. + * It searches only the class of self. + * + * @throws RuntimeException if the method is not found. + */ + public static Method findMethod(Class clazz, String name, String desc) { + Method m = findMethod2(clazz, name, desc); + if (m == null) + error(clazz, name, desc); + + return m; + } + + /** + * Finds a method that has the given name and descriptor and is declared + * in the super class. + * + * @throws RuntimeException if the method is not found. + */ + public static Method findSuperMethod(Object self, String name, String desc) { + // for JBoss Seam. See JASSIST-183. + Class clazz = self.getClass(); + return findSuperClassMethod(clazz, name, desc); + } + + /** + * Finds a method that has the given name and descriptor and is declared + * in the super class. + * + * @throws RuntimeException if the method is not found. + */ + public static Method findSuperClassMethod(Class clazz, String name, String desc) { + Method m = findSuperMethod2(clazz.getSuperclass(), name, desc); + if (m == null) + m = searchInterfaces(clazz, name, desc); + + if (m == null) + error(clazz, name, desc); + + return m; + } + + private static void error(Class clazz, String name, String desc) { + throw new RuntimeException("not found " + name + ":" + desc + + " in " + clazz.getName()); + } + + private static Method findSuperMethod2(Class clazz, String name, String desc) { + Method m = findMethod2(clazz, name, desc); + if (m != null) + return m; + + Class superClass = clazz.getSuperclass(); + if (superClass != null) { + m = findSuperMethod2(superClass, name, desc); + if (m != null) + return m; + } + + return searchInterfaces(clazz, name, desc); + } + + private static Method searchInterfaces(Class clazz, String name, String desc) { + Method m = null; + Class[] interfaces = clazz.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + m = findSuperMethod2(interfaces[i], name, desc); + if (m != null) + return m; + } + + return m; + } + + private static Method findMethod2(Class clazz, String name, String desc) { + Method[] methods = SecurityActions.getDeclaredMethods(clazz); + int n = methods.length; + for (int i = 0; i < n; i++) + if (methods[i].getName().equals(name) + && makeDescriptor(methods[i]).equals(desc)) + return methods[i]; + + return null; + } + + /** + * Makes a descriptor for a given method. + */ + public static String makeDescriptor(Method m) { + Class[] params = m.getParameterTypes(); + return makeDescriptor(params, m.getReturnType()); + } + + /** + * Makes a descriptor for a given method. + * + * @param params parameter types. + * @param retType return type. + */ + public static String makeDescriptor(Class[] params, Class retType) { + StringBuffer sbuf = new StringBuffer(); + sbuf.append('('); + for (int i = 0; i < params.length; i++) + makeDesc(sbuf, params[i]); + + sbuf.append(')'); + if (retType != null) + makeDesc(sbuf, retType); + + return sbuf.toString(); + } + + /** + * Makes a descriptor for a given method. + * + * @param params the descriptor of parameter types. + * @param retType return type. + */ + public static String makeDescriptor(String params, Class retType) { + StringBuffer sbuf = new StringBuffer(params); + makeDesc(sbuf, retType); + return sbuf.toString(); + } + + private static void makeDesc(StringBuffer sbuf, Class type) { + if (type.isArray()) { + sbuf.append('['); + makeDesc(sbuf, type.getComponentType()); + } + else if (type.isPrimitive()) { + if (type == Void.TYPE) + sbuf.append('V'); + else if (type == Integer.TYPE) + sbuf.append('I'); + else if (type == Byte.TYPE) + sbuf.append('B'); + else if (type == Long.TYPE) + sbuf.append('J'); + else if (type == Double.TYPE) + sbuf.append('D'); + else if (type == Float.TYPE) + sbuf.append('F'); + else if (type == Character.TYPE) + sbuf.append('C'); + else if (type == Short.TYPE) + sbuf.append('S'); + else if (type == Boolean.TYPE) + sbuf.append('Z'); + else + throw new RuntimeException("bad type: " + type.getName()); + } + else + sbuf.append('L').append(type.getName().replace('.', '/')) + .append(';'); + } + + /** + * Converts a proxy object to an object that is writable to an + * object stream. This method is called by writeReplace() + * in a proxy class. + * + * @since 3.4 + */ + public static SerializedProxy makeSerializedProxy(Object proxy) + throws java.io.InvalidClassException + { + Class clazz = proxy.getClass(); + + MethodHandler methodHandler = null; + if (proxy instanceof ProxyObject) + methodHandler = ((ProxyObject)proxy).getHandler(); + else if (proxy instanceof Proxy) + methodHandler = ProxyFactory.getHandler((Proxy)proxy); + + return new SerializedProxy(clazz, ProxyFactory.getFilterSignature(clazz), methodHandler); + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java new file mode 100644 index 0000000..d8e8a77 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java @@ -0,0 +1,136 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ +package com.wenshuo.agent.javassist.util.proxy; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +class SecurityActions { + static Method[] getDeclaredMethods(final Class clazz) { + if (System.getSecurityManager() == null) + return clazz.getDeclaredMethods(); + else { + return (Method[]) AccessController + .doPrivileged(new PrivilegedAction() { + public Object run() { + return clazz.getDeclaredMethods(); + } + }); + } + } + + static Constructor[] getDeclaredConstructors(final Class clazz) { + if (System.getSecurityManager() == null) + return clazz.getDeclaredConstructors(); + else { + return (Constructor[]) AccessController + .doPrivileged(new PrivilegedAction() { + public Object run() { + return clazz.getDeclaredConstructors(); + } + }); + } + } + + static Method getDeclaredMethod(final Class clazz, final String name, + final Class[] types) throws NoSuchMethodException { + if (System.getSecurityManager() == null) + return clazz.getDeclaredMethod(name, types); + else { + try { + return (Method) AccessController + .doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + return clazz.getDeclaredMethod(name, types); + } + }); + } + catch (PrivilegedActionException e) { + if (e.getCause() instanceof NoSuchMethodException) + throw (NoSuchMethodException) e.getCause(); + + throw new RuntimeException(e.getCause()); + } + } + } + + static Constructor getDeclaredConstructor(final Class clazz, + final Class[] types) + throws NoSuchMethodException + { + if (System.getSecurityManager() == null) + return clazz.getDeclaredConstructor(types); + else { + try { + return (Constructor) AccessController + .doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + return clazz.getDeclaredConstructor(types); + } + }); + } + catch (PrivilegedActionException e) { + if (e.getCause() instanceof NoSuchMethodException) + throw (NoSuchMethodException) e.getCause(); + + throw new RuntimeException(e.getCause()); + } + } + } + + static void setAccessible(final AccessibleObject ao, + final boolean accessible) { + if (System.getSecurityManager() == null) + ao.setAccessible(accessible); + else { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + ao.setAccessible(accessible); + return null; + } + }); + } + } + + static void set(final Field fld, final Object target, final Object value) + throws IllegalAccessException + { + if (System.getSecurityManager() == null) + fld.set(target, value); + else { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + fld.set(target, value); + return null; + } + }); + } + catch (PrivilegedActionException e) { + if (e.getCause() instanceof NoSuchMethodException) + throw (IllegalAccessException) e.getCause(); + + throw new RuntimeException(e.getCause()); + } + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java b/src/main/java/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java new file mode 100644 index 0000000..70e6469 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java @@ -0,0 +1,99 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package com.wenshuo.agent.javassist.util.proxy; + +import java.io.Serializable; +import java.io.ObjectStreamException; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; + +/** + * A proxy object is converted into an instance of this class + * when it is written to an output stream. + * + * @see RuntimeSupport#makeSerializedProxy(Object) + */ +class SerializedProxy implements Serializable { + private String superClass; + private String[] interfaces; + private byte[] filterSignature; + private MethodHandler handler; + + SerializedProxy(Class proxy, byte[] sig, MethodHandler h) { + filterSignature = sig; + handler = h; + superClass = proxy.getSuperclass().getName(); + Class[] infs = proxy.getInterfaces(); + int n = infs.length; + interfaces = new String[n - 1]; + String setterInf = ProxyObject.class.getName(); + String setterInf2 = Proxy.class.getName(); + for (int i = 0; i < n; i++) { + String name = infs[i].getName(); + if (!name.equals(setterInf) && !name.equals(setterInf2)) + interfaces[i] = name; + } + } + + /** + * Load class. + * + * @param className the class name + * @return loaded class + * @throws ClassNotFoundException for any error + */ + protected Class loadClass(final String className) throws ClassNotFoundException { + try { + return (Class)AccessController.doPrivileged(new PrivilegedExceptionAction(){ + public Object run() throws Exception{ + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return Class.forName(className, true, cl); + } + }); + } + catch (PrivilegedActionException pae) { + throw new RuntimeException("cannot load the class: " + className, pae.getException()); + } + } + + Object readResolve() throws ObjectStreamException { + try { + int n = interfaces.length; + Class[] infs = new Class[n]; + for (int i = 0; i < n; i++) + infs[i] = loadClass(interfaces[i]); + + ProxyFactory f = new ProxyFactory(); + f.setSuperclass(loadClass(superClass)); + f.setInterfaces(infs); + Proxy proxy = (Proxy)f.createClass(filterSignature).newInstance(); + proxy.setHandler(handler); + return proxy; + } + catch (ClassNotFoundException e) { + throw new java.io.InvalidClassException(e.getMessage()); + } + catch (InstantiationException e2) { + throw new java.io.InvalidObjectException(e2.getMessage()); + } + catch (IllegalAccessException e3) { + throw new java.io.InvalidClassException(e3.getMessage()); + } + } +} diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/package.html b/src/main/java/com/wenshuo/agent/javassist/util/proxy/package.html new file mode 100644 index 0000000..6c77804 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/javassist/util/proxy/package.html @@ -0,0 +1,6 @@ + + +Dynamic proxy (similar to Enhancer of cglib). +See ProxyFactory for more details. + + diff --git a/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java b/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java new file mode 100644 index 0000000..c1f9678 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java @@ -0,0 +1,316 @@ +/* + * @(#)ExecuteLogUtils.java 2015-7-27 下午05:57:01 javaagent Copyright 2015 wenshuo, Inc. All rights reserved. wenshuo + * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ +package com.wenshuo.agent.log; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.RandomAccessFile; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import com.wenshuo.agent.AgentUtils; +import com.wenshuo.agent.ConfigUtils; +import com.wenshuo.agent.NamedThreadFactory; + +/** + * ExecuteLogUtils + * + * @author dingjsh + * @time 2015-7-27下午05:57:01 + */ +public class ExecuteLogUtils { + + private static BufferedWriter counterLogWriter = null; + + private static long nextDayStartTimeMillis; + + private static String logFileName = null; + + private static long startTimeMillis; + + private static ScheduledThreadPoolExecutor counterLogExecutor; + + private static ConcurrentHashMap> executeCounterMap; + + private static final Object executeCounterLock = new Object(); + + private static final int BUFFER_SIZE = 256 * 1024; + + private static final String ENCODING = "UTF-8"; + + private static boolean isUsingNanoTime = false; + + private static boolean logAvgExecuteTime = false; + + private static volatile boolean isInitialized = false; + + private ExecuteLogUtils() { + super(); + } + + /** + * 初使化 + * + * @author dingjsh + * @time 2015-7-30上午10:47:58 + */ + public static synchronized void init() { + if (isInitialized) { + return; + } + logFileName = ConfigUtils.getLogFileName(); + int interval = ConfigUtils.getLogInterval(); + logAvgExecuteTime = ConfigUtils.isLogAvgExecuteTime(); + isUsingNanoTime = ConfigUtils.isUsingNanoTime(); + if (AgentUtils.isBlank(logFileName)) { + System.err.println("日志文件名为空"); + throw new RuntimeException("日志文件名为空"); + } + setNextDateStartTimeMillis(); + initWriter(); + executeCounterMap = new ConcurrentHashMap>(); + startTimeMillis = System.currentTimeMillis(); + counterLogExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("pool-thread-agent-log", true)); + counterLogExecutor.scheduleWithFixedDelay(new OutputLogRunnable(), interval, interval, TimeUnit.SECONDS); + isInitialized = true; + } + + public static void log(String className, String methodName, long executeTime) { + logExecuteCounter(className, methodName, executeTime); + } + + /** + * 输出方法执行记数日志 + * + * @author dingjsh + * @time 2015-7-28下午01:35:25 + */ + synchronized static void outputCounterLog() throws IOException { + ConcurrentHashMap> executeCounterMapInner = executeCounterMap; + executeCounterMap = new ConcurrentHashMap>(); + String startTime = formatTimeMillis(startTimeMillis); + String endTime = formatTimeMillis(System.currentTimeMillis()); + long byteLength = removeJSONArrayEndBracket(); + if (0 == byteLength) { + writeLog("[", true, startTimeMillis); + } + + Set>> entrySet = executeCounterMapInner.entrySet(); + Iterator>> ite = entrySet.iterator(); + int length = entrySet.size(); + if (length > 0 && byteLength > 10) { // 说明文件不只是[],json数组中已经有内容 + writeLog(",", startTimeMillis); + } + for (int index = 0; ite.hasNext(); index++) { + Map.Entry> entry = ite.next(); + String className = entry.getKey(); + Map method2ExecuteMap = entry.getValue(); + String methodExecuteJson = MethodExecuteJSONformatter.getMethodExecuteJSON(className, method2ExecuteMap, + startTime, endTime, isUsingNanoTime, logAvgExecuteTime); + writeLog(methodExecuteJson, startTimeMillis); + if (index < length - 1) { + writeLog(",", true, startTimeMillis); + } + } + writeLog("]", true, startTimeMillis); + flushLogAndClose(); + startTimeMillis = System.currentTimeMillis(); + } + + private static void logExecuteCounter(String className, String methodName, long executeTime) { + // dingjs modified in 20210825 原来是用锁和AtomicLong来避免线程安全问题,现在去掉这些实现。原因是此处线程安全问题顶多导致 + // 数据有一些误差,这个误差对于agent数据监控并没有什么影响,牺牲一定的准确性带来性能的提升是有必要的 + ConcurrentHashMap methodCounterMap = getOrCreateClassExecutesMapping(className); + long[] counter = methodCounterMap.get(methodName); + if (null == counter) { + methodCounterMap.put(methodName, new long[]{1, executeTime}); + } else { + counter[0]++; + counter[1] = executeTime + counter[1]; + } + } + + /** + * ExecuteLogUtils + * + * @param className 类名 + * @return class和它的调用次数映射关系 + * @description 获得class和它的调用次数映射关系,如果exeuteCounterMap中没有,则创建一个并放入 + * @author dingjsh + * @date 2018年5月25日 下午4:40:33 + * @version 1.2.0 + */ + private static ConcurrentHashMap getOrCreateClassExecutesMapping(String className) { + ConcurrentHashMap methodCounterMap = executeCounterMap.get(className); + if (null == methodCounterMap) { + synchronized (executeCounterLock) { + methodCounterMap = executeCounterMap.get(className); + if (null == methodCounterMap) { + methodCounterMap = new ConcurrentHashMap(); + executeCounterMap.put(className, methodCounterMap); + } + } + } + return methodCounterMap; + } + + private static void writeLog(String logValue, long currTimeMillis) { + writeLog(logValue, false, currTimeMillis); + } + + private static void writeLog(String logValue, boolean newLine, long currTimeMillis) { + ensureLogFileUpToDate(currTimeMillis); + try { + counterLogWriter.write(logValue); + if (newLine) { + counterLogWriter.newLine(); + } + } catch (IOException e) { + System.err.println(e); + } + + } + + private static void flushLogAndClose() { + try { + counterLogWriter.flush(); + } catch (IOException e) { + System.err.println(e); + } finally { + AgentUtils.closeQuietly(counterLogWriter); + counterLogWriter = null; + } + } + + /** + * 确保日志文件没过时,日志文件都会加上日期后缀,如果当前日志文件 + */ + private static void ensureLogFileUpToDate(long currTimeMillis) { + if (currTimeMillis >= nextDayStartTimeMillis) { + try { + if (null != counterLogWriter) { + counterLogWriter.flush(); + } + } catch (Exception e) { + System.err.println(e); + } finally { + AgentUtils.closeQuietly(counterLogWriter); + } + initWriter(); + setNextDateStartTimeMillis(); + } + if (null == counterLogWriter) { + initWriter(); + } + + } + + private static void initWriter() { + try { + File logFile = getCounterLogFile(logFileName); + counterLogWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile, true), ENCODING), + BUFFER_SIZE); + } catch (IOException e) { + System.err.println(e); + throw new RuntimeException("无法初始化【" + logFileName + "】,建议您检查磁盘空间,或者手动删除该日志文件"); + } + } + + private static File getCounterLogFile(String logFileName) throws IOException { + + String logFileNameWithDate = getCurrDateString(); + int lastIndexOfDot = logFileName.lastIndexOf('.'); + if (lastIndexOfDot > 0) { + logFileName = logFileName.substring(0, lastIndexOfDot) + "." + logFileNameWithDate + + logFileName.substring(lastIndexOfDot); + } else { + logFileName = logFileName + "." + logFileNameWithDate + ".log"; + } + File logFile = getLogFile(logFileName); + return logFile; + } + + private static File getLogFile(String logFileName) throws IOException { + File logFile = new File(logFileName); + if (logFile.isDirectory()) { + System.err.println("【" + logFileName + "】是文件夹"); + throw new RuntimeException("【" + logFileName + "】是文件夹"); + } else if (!logFile.exists()) { + // dingjs modified in 20140513 + File dirFile = logFile.getParentFile(); + AgentUtils.forceMkdir(dirFile); + boolean created = logFile.createNewFile(); + if (!created) { + System.err.println("【" + logFileName + "】创建失败"); + throw new RuntimeException("【" + logFileName + "】创建失败"); + } + } + return logFile; + } + + private static String getCurrDateString() { + Date today = new Date(); + return new SimpleDateFormat("yyyy-MM-dd").format(today); + } + + /** + * 由开始日期转换为开始时间,一般在以时间为条件的查询中使用 + */ + private static Date date2StartTime(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + String dateStr = sdf.format(date); + Date startTime = null; + try { + startTime = sdf.parse(dateStr + " 00:00:00"); + } catch (ParseException e) { + System.err.println(e); + } + return startTime; + } + + /** + * 设置下一天的开始时间 + */ + private static void setNextDateStartTimeMillis() { + Date currDate = new Date(); + Calendar cal = Calendar.getInstance(); + cal.setTime(currDate); + cal.add(Calendar.DATE, 1); + Date startTime = date2StartTime(cal.getTime()); + nextDayStartTimeMillis = startTime.getTime(); + } + + private static String formatTimeMillis(long timeMillis) { + Date date = new Date(timeMillis); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return sdf.format(date); + } + + private static long removeJSONArrayEndBracket() throws IOException { + File logFile = getCounterLogFile(logFileName); + RandomAccessFile f = new RandomAccessFile(logFile.getAbsolutePath(), "rw"); + long length = f.length(); + byte b = -1; + while (b != 93 && length > 0) { + length -= 1; + f.seek(length); + b = f.readByte(); + } + f.setLength(length); + f.close(); + return length; + } +} \ No newline at end of file diff --git a/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java b/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java new file mode 100644 index 0000000..13e1454 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java @@ -0,0 +1,70 @@ +package com.wenshuo.agent.log; + +import java.util.Map; + + +/** + * MethodExecuteJSONformatter + * + * @author dingjsh + * @version 2.0.0 + * @description 2.0.0 + * @date 2018年8月22日 下午8:07:01 + */ +class MethodExecuteJSONformatter { + + /** + * MethodExecuteJSONformatter + * + * @param className 类名 + * @param method2ExecuteMap Map<方法名, Long[]> 数组第一个是执行次数,第二个是执行时间 + * @param startTime 统计周期的开始时间,格式为 yyyy-MM-dd HH:mm:ss + * @param endTime 统计周期的结束时间,格式为 yyyy-MM-dd HH:mm:ss + * @return 格式化后的json string + * @description 将方法执行监控数据格式化为json string + * @author dingjsh + * @date 2018年8月22日 下午8:49:18 + * @version 2.0.0 + */ + static String getMethodExecuteJSON(String className, Map method2ExecuteMap, + String startTime, String endTime, boolean isUsingNanoTime, boolean logAvgExecuteTime) { + StringBuilder json = new StringBuilder("{"); + appendString(json, "class", className).append(","); + appendString(json, "start", startTime).append(","); + appendString(json, "end", endTime).append(","); + appendKey(json, "methods").append("["); + for (Map.Entry methodEntry : method2ExecuteMap.entrySet()) { + String methodName = methodEntry.getKey(); + long[] executeCounter = methodEntry.getValue(); + long counter = executeCounter[0]; + long timeInMillis + = isUsingNanoTime ? executeCounter[1] / 1000000 : executeCounter[1]; + json.append("{"); + appendString(json, "name", methodName).append(","); + appendLong(json, "counter", counter).append(","); + appendLong(json, "time", timeInMillis); + if (logAvgExecuteTime && counter > 0) { + json.append(","); + appendLong(json, "avg", timeInMillis / counter); + } + json.append("},"); + } + if (json.charAt(json.length() - 1) == ',') { + json.deleteCharAt(json.length() - 1); + } + json.append("]}"); + return json.toString(); + } + + private static StringBuilder appendString(StringBuilder jsonBuilder, String key, String value) { + return appendKey(jsonBuilder, key).append("\"").append(value).append("\""); + } + + private static StringBuilder appendLong(StringBuilder jsonBuilder, String key, long value) { + return appendKey(jsonBuilder, key).append(value); + } + + private static StringBuilder appendKey(StringBuilder jsonBuilder, String key) { + return jsonBuilder.append("\"").append(key).append("\":"); + } +} diff --git a/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java b/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java new file mode 100644 index 0000000..3a08620 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java @@ -0,0 +1,31 @@ +/* + * @(#)OutputLogRunnable.java 2015-7-28 下午03:27:20 + * javaagent + * Copyright 2015 wenshuo, Inc. All rights reserved. + * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ +package com.wenshuo.agent.log; + +/** + * OutputLogRunnable + * + * @author dingjsh + * @time 2015-7-28下午03:27:20 + */ +public class OutputLogRunnable implements Runnable { + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + ExecuteLogUtils.outputCounterLog(); + }catch(Exception e){ + System.err.println(e); + } + } + +} diff --git a/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java b/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java new file mode 100644 index 0000000..545b231 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java @@ -0,0 +1,155 @@ +package com.wenshuo.agent.transformer; + +import com.wenshuo.agent.ConfigUtils; +import com.wenshuo.agent.PojoDetector; +import com.wenshuo.agent.javassist.CannotCompileException; +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.LoaderClassPath; +import com.wenshuo.agent.javassist.Modifier; +import com.wenshuo.agent.javassist.NotFoundException; +import java.io.IOException; +import java.lang.instrument.ClassFileTransformer; +import java.security.ProtectionDomain; +import java.util.Collections; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * AgentLogClassFileTransformer 类增强,增加agent日志 + * + * @author dingjsh + * @time 2015-7-24上午09:53:44 + */ +public class AgentLogClassFileTransformer implements ClassFileTransformer { + + private static final String LOG_UTILS = "com.wenshuo.agent.log.ExecuteLogUtils"; + + private static final String AGENT_PACKAGE_NAME = "com.wenshuo.agent"; + + + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classfileBuffer) { + byte[] byteCode = classfileBuffer; + className = className.replace('/', '.'); + if (!isNeedLogExecuteInfo(className)) { + return byteCode; + } + if (null == loader) { + loader = Thread.currentThread().getContextClassLoader(); + } + byteCode = aopLog(loader, className, byteCode); + return byteCode; + } + + private byte[] aopLog(ClassLoader loader, String className, byte[] byteCode) { + try { + ClassPool cp = ClassPool.getDefault(); + CtClass cc; + try { + cc = cp.get(className); + } catch (NotFoundException e) { + cp.insertClassPath(new LoaderClassPath(loader)); + cc = cp.get(className); + } + byteCode = aopLog(cc, className, byteCode); + } catch (Exception ex) { + System.err.println(ex); + } + return byteCode; + } + + private byte[] aopLog(CtClass cc, String className, byte[] byteCode) throws CannotCompileException, IOException { + if (null == cc) { + return byteCode; + } + if (!cc.isInterface()) { + CtMethod[] methods = cc.getDeclaredMethods(); + if (null != methods && methods.length > 0) { + boolean isOpenPojoMonitor = ConfigUtils.isOpenPojoMonitor(); + Set getSetMethods = Collections.emptySet(); + if (!isOpenPojoMonitor) { + getSetMethods = PojoDetector.getPojoMethodNames(methods); + } + for (CtMethod m : methods) { + if (isOpenPojoMonitor || !getSetMethods.contains(m.getName())) { + aopLog(className, m); + } + } + byteCode = cc.toBytecode(); + } + } + cc.detach(); + return byteCode; + } + + private void aopLog(String className, CtMethod m) throws CannotCompileException { + if (null == m || m.isEmpty()) { + return; + } + boolean isMethodStatic = Modifier.isStatic(m.getModifiers()); + String aopClassName = isMethodStatic ? "\"" + className + "\"" : "this.getClass().getName()"; + final String timeMethodStr + = ConfigUtils.isUsingNanoTime() ? "java.lang.System.nanoTime()" : "java.lang.System.currentTimeMillis()"; + + // 避免变量名重复 + m.addLocalVariable("dingjsh_javaagent_elapsedTime", CtClass.longType); + m.insertBefore("dingjsh_javaagent_elapsedTime = " + timeMethodStr + ";"); + m.insertAfter("dingjsh_javaagent_elapsedTime = " + timeMethodStr + " - dingjsh_javaagent_elapsedTime;"); + m.insertAfter(LOG_UTILS + ".log(" + aopClassName + ",\"" + m.getName() + + "\",(long)dingjsh_javaagent_elapsedTime" + ");"); + } + + /** + * 是否需要记录执行信息 + * + * @param className 类名 + * @return 是否需要记录执行信息 + * @author dingjsh + * @time 2015-7-27下午06:11:02 + */ + private boolean isNeedLogExecuteInfo(String className) { + // do not transform the agent class,prevent deadlock + if (className.startsWith(AGENT_PACKAGE_NAME)) { + return false; + } + Set includes = ConfigUtils.getIncludePackages(); + if (null == includes || includes.isEmpty()) { + return false; + } + + boolean isNeeded = false; + // include package + for (String packageName : includes) { + if (className.startsWith(packageName)) { + isNeeded = true; + break; + } + } + // exclude package + if (isNeeded) { + Set excludes = ConfigUtils.getExcludePackages(); + if (null != excludes && !excludes.isEmpty()) { + for (String packageName : excludes) { + if (className.startsWith(packageName)) { + isNeeded = false; + break; + } + } + } + } + if (isNeeded) { + Set excludeClassRegexs = ConfigUtils.getExcludeClassRegexs(); + if (null != excludeClassRegexs && !excludeClassRegexs.isEmpty()) { + for (String regex : excludeClassRegexs) { + isNeeded = !Pattern.matches(regex, className); + if (!isNeeded) { + break; + } + } + } + } + return isNeeded; + } +} diff --git a/src/main/resources/agent.properties b/src/main/resources/agent.properties new file mode 100644 index 0000000..2117cd7 --- /dev/null +++ b/src/main/resources/agent.properties @@ -0,0 +1,16 @@ +#\u9700\u8981\u76d1\u63a7\u7684\u5305\uff0c\u591a\u4e2a\u5305\u7528\u5206\u53f7\u5206\u9694 +agent.include.package=com.XXX +#\u4e0d\u9700\u8981\u76d1\u63a7\u7684\u5305\uff0c\u591a\u4e2a\u5305\u7528\u5206\u53f7\u5206\u9694\u3002 \u9700\u8981\u76d1\u63a7\u7684\u5305-\u4e0d\u9700\u8981\u76d1\u63a7\u7684\u5305\u5c31\u662f\u771f\u6b63\u8981\u76d1\u63a7\u7684\u5305 +agent.exclude.package= +#\u65e5\u5fd7\u6587\u4ef6\uff0c\u4f1a\u81ea\u52a8\u589e\u52a0\u65e5\u671f\u540e\u7f00 +agent.log.file=d\:\\agent.log +#\u65e5\u5fd7\u8f93\u51fa\u5468\u671f +agent.log.interval.seconds=600 +#\u4e0d\u9700\u8981\u76d1\u63a7\u7684\u7c7b\u7684\u6b63\u5219\u8868\u8fbe\u5f0f +agent.exclude.class.regex= +#\u662f\u5426\u8bb0\u5f55\u5e73\u5747\u65f6\u95f4 +agent.log.avg.execute.time=false +#\u9ed8\u8ba4\u4e0d\u9700\u8981\u76d1\u63a7\u7684\u7c7b\u7684\u6b63\u5219\u8868\u8fbe\u5f0f +agent.exclude.class.regex.default=.*EnhancerByCGLIB.*;.*FastClassByCGLIB.*;.*EnhancerBySpringCGLIB.*;.*FastClassBySpringCGLIB.* +#\u8bb0\u5f55\u65b9\u6cd5\u7684\u8017\u65f6\u65f6\u662f\u91c7\u7528nanoTime,\u8fd8\u662fcurrentTimeMillis,nanoTime\u66f4\u51c6\u786e\uff0c\u4f46\u662f\u4f1a\u8017\u65f6\u4e00\u4e9b +agent.log.nano=true diff --git a/src/test/java/com/wenshuo/agent/test/TestCtMethod.java b/src/test/java/com/wenshuo/agent/test/TestCtMethod.java new file mode 100644 index 0000000..ecca591 --- /dev/null +++ b/src/test/java/com/wenshuo/agent/test/TestCtMethod.java @@ -0,0 +1,50 @@ +package com.wenshuo.agent.test; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.wenshuo.agent.javassist.ClassPool; +import com.wenshuo.agent.javassist.CtClass; +import com.wenshuo.agent.javassist.CtMethod; +import com.wenshuo.agent.javassist.Modifier; +import com.wenshuo.agent.javassist.NotFoundException; + +public class TestCtMethod { + + private static List staticMethodNames = Arrays.asList("privateStatic","protectedStatic","publicStatic","defaultStatic"); + + @Test + public void staticMethodTest() throws NotFoundException{ + ClassPool cp = ClassPool.getDefault(); + CtClass ctClass = cp.get("com.wenshuo.agent.test.TestCtMethod"); + CtMethod[] ctMethods = ctClass.getDeclaredMethods(); + for(CtMethod ctMethod : ctMethods){ + String methodName = ctMethod.getName(); + if(staticMethodNames.contains(methodName)){ + Assert.assertTrue(methodName+" is static method", Modifier.isStatic(ctMethod.getModifiers())); + } + } + System.out.println("staticMethodTest is successful"); + } + + private static void privateStatic(){ + System.out.println("I'm private and static"); + } + + protected static void protectedStatic(){ + System.out.println("I'm private and static"); + } + + public static void publicStatic(){ + System.out.println("I'm public and static"); + } + static void defaultStatic(){ + System.out.println("I'm default and static"); + } + + + +} From 2a13f489054f622aae42010e349444635610ea33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=BB=BA=E6=B0=B4?= Date: Mon, 16 Oct 2023 11:07:55 +0800 Subject: [PATCH 17/25] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8736aa2..84f08d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Javaagent +# 🎉 Javaagent 🎉 ## 概述 -javaagent是一个简单优雅的java agent,利用java自带的instrument特性+javassist字节码编辑技术,实现了无侵入的方法级性能监控。相比于NewRelic或者开源的[pinpoint](https://github.com/naver/pinpoint),以及阿里的[arthas](https://github.com/alibaba/arthas),本工具主打的是简单,我们只记录每个方法的执行次数和时间,并输出到json格式的日志文件中。基于javaagent的日志,你可以使用严丽同学开发的[agent日志分析工具](https://pan.baidu.com/s/1Ma4iEWRmBonGO1TapeEF-g)进行分析查询,或者可以自己去写分析器,这样可以让你快速定位生产环境的性能瓶颈。 +**javaagent** 是一个简单优雅的 java agent ,利用 java 自带的 `instrument` 特性+ `javassist` 字节码编辑技术,实现了无侵入的方法级性能监控。相比于NewRelic或者开源的[pinpoint](https://github.com/naver/pinpoint),以及阿里的[arthas](https://github.com/alibaba/arthas),本工具主打的是简单,我们只记录每个方法的执行次数和时间,并输出到json格式的日志文件中。基于javaagent的日志,你可以使用严丽同学开发的[agent日志分析工具](https://pan.baidu.com/s/1Ma4iEWRmBonGO1TapeEF-g)进行分析查询,或者可以自己去写分析器,这样可以让你快速定位生产环境的性能瓶颈。 ## 集成 java启动参数中就有javaagent,你只需要在JAVA_OPTS中加入`-javaagent:/opt/javaagent/javaagent.jar=/opt/javaagent/agent.properties`就实现了方法级监控。其中`=`前指定的是jar包的路径,`=`后指定的是对agent的一些配置参数。 @@ -12,25 +12,24 @@ agent.include.package=com.XXX # 不需要监控的包,多个包用分号分隔。 需要监控的包-不需要监控的包就是真正要监控的包 agent.exclude.package= # 日志文件,会自动增加日期后缀 -agent.log.file=d\:\\agent.log +agent.log.file=/opt/agent.log # 日志输出周期 agent.log.interval.seconds=600 # 不需要监控的类的正则表达式 agent.exclude.class.regex= # 是否记录平均时间 agent.log.avg.execute.time=false -# 默认不需要监控的类的正则表达式 -agent.exclude.class.regex.default=.*EnhancerByCGLIB.*;.*FastClassByCGLIB.* # 记录方法的耗时时是采用nanoTime,还是currentTimeMillis,nanoTime更准确,但是会耗时一些 agent.log.nano=true ``` ## agent日志分析器使用 -agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分析工具,除了基础功能,对于有sql能力的同学,你可以直接使用[agent日志分析工具](https://pan.baidu.com/s/1Ma4iEWRmBonGO1TapeEF-g)自带的[console](http://XXXX/console)登录后,直接写sql进行统计,你可以按自己想要的维度来进行处理。 +agent日志分析器是我同事严丽使用SpringBoot+h2数据库开发的分析工具,除了基础功能,对于有sql能力的同学,你可以直接使用[agent日志分析工具](https://pan.baidu.com/s/1Ma4iEWRmBonGO1TapeEF-g)自带的[console](http://ip:port/console)登录后,直接写sql进行统计,你可以按自己想要的维度来进行处理。 - JDBC URL: jdbc:h2:mem:~/h2test - USer Name : sa - Password : 空 -登录后,你就可以尽情的写sql了。 + +登录后,你就可以尽情地写 sql 对类、方法的执行次数、执行时间进行统计查询。 ## 欢迎关注我的公众号 - 程序员阿水 From edf5e4ee08d2f8da4130518e0cf22368a33426f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=BB=BA=E6=B0=B4?= Date: Mon, 16 Oct 2023 11:21:01 +0800 Subject: [PATCH 18/25] =?UTF-8?q?=E4=BF=AE=E6=94=B9MANIFEST?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4bc2299..d3d4eec 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + https://www.apache.org/licenses/LICENSE-2.0.txt repo @@ -48,9 +48,10 @@ org.apache.maven.plugins maven-compiler-plugin + 3.8.1 - 6 - 6 + ${project.build.target} + ${project.build.target} From aa1cea60d0042f58977fb5d42c16afca92ad589d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=BB=BA=E6=B0=B4?= Date: Mon, 16 Oct 2023 17:59:27 +0800 Subject: [PATCH 19/25] =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E5=BC=95=E7=94=A8=E7=9A=84=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 146 ++++++++++-------- src/main/java/com/wenshuo/agent/Agent.java | 9 +- .../java/com/wenshuo/agent/ConfigUtils.java | 10 +- .../wenshuo/agent/applog/AppLogFactory.java | 52 +++++++ .../agent/applog/CommonsLoggingAppLog.java | 50 ++++++ .../wenshuo/agent/applog/ConsoleAppLog.java | 40 +++++ .../com/wenshuo/agent/applog/IAppLog.java | 28 ++++ .../com/wenshuo/agent/applog/Slf4jAppLog.java | 50 ++++++ .../wenshuo/agent/log/ExecuteLogUtils.java | 20 ++- .../wenshuo/agent/log/OutputLogRunnable.java | 19 +-- .../AgentLogClassFileTransformer.java | 30 ++-- 11 files changed, 357 insertions(+), 97 deletions(-) create mode 100644 src/main/java/com/wenshuo/agent/applog/AppLogFactory.java create mode 100644 src/main/java/com/wenshuo/agent/applog/CommonsLoggingAppLog.java create mode 100644 src/main/java/com/wenshuo/agent/applog/ConsoleAppLog.java create mode 100644 src/main/java/com/wenshuo/agent/applog/IAppLog.java create mode 100644 src/main/java/com/wenshuo/agent/applog/Slf4jAppLog.java diff --git a/pom.xml b/pom.xml index d3d4eec..7a93d28 100644 --- a/pom.xml +++ b/pom.xml @@ -1,76 +1,90 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 - com.wenshuo - javaagent - 2.1.2 - jar + com.wenshuo + javaagent + 2.1.2 + jar - javaagent - 基于javaagent技术来记录方法执行次数和时间 - https://github.com/dingjs/javaagent + javaagent + 基于javaagent技术来记录方法执行次数和时间 + https://github.com/dingjs/javaagent - - UTF-8 - 1.6 - UTF-8 - - - - dingjsh - 丁建水 - 47954233@qq.com - - - - - Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - repo - - + + UTF-8 + 1.6 + UTF-8 + + + + dingjsh + 丁建水 + 47954233@qq.com + + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + - - - - org.apache.maven.plugins - maven-jar-plugin - 3.3.0 - - - - com.wenshuo.agent.Agent - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - ${project.build.target} - ${project.build.target} - - - - + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + com.wenshuo.agent.Agent + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${project.build.target} + ${project.build.target} + + + + - + + + com.sun + tools + ${java.version} + system + true + ${java.home}/../lib/tools.jar + + + junit + junit + 4.13.2 + test + + + org.slf4j + slf4j-api + 1.7.36 + compile + true + - com.sun - tools - ${java.version} - system + commons-logging + commons-logging + 1.2 + compile true - ${java.home}/../lib/tools.jar - - junit - junit - 4.13.2 - test - - + diff --git a/src/main/java/com/wenshuo/agent/Agent.java b/src/main/java/com/wenshuo/agent/Agent.java index c32f651..4397784 100644 --- a/src/main/java/com/wenshuo/agent/Agent.java +++ b/src/main/java/com/wenshuo/agent/Agent.java @@ -4,8 +4,13 @@ * Copyright 2015 wenshuo, Inc. All rights reserved. * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ + package com.wenshuo.agent; + import java.lang.instrument.Instrumentation; + +import com.wenshuo.agent.applog.AppLogFactory; +import com.wenshuo.agent.applog.IAppLog; import com.wenshuo.agent.log.ExecuteLogUtils; import com.wenshuo.agent.transformer.AgentLogClassFileTransformer; @@ -16,10 +21,12 @@ */ public class Agent { + private static IAppLog log = AppLogFactory.getAppLog(Agent.class); + public static void premain(String agentArs, Instrumentation inst) { - System.out.println("javaagent启动成功,将自动记录方法的执行次数和时间"); // 初始化配置 ConfigUtils.initProperties(agentArs); + log.info("javaagent启动成功,将自动记录方法的执行次数和时间,日志文件路径:" + ConfigUtils.getLogFileName()); ExecuteLogUtils.init(); inst.addTransformer(new AgentLogClassFileTransformer()); } diff --git a/src/main/java/com/wenshuo/agent/ConfigUtils.java b/src/main/java/com/wenshuo/agent/ConfigUtils.java index 400a9be..e086fc2 100644 --- a/src/main/java/com/wenshuo/agent/ConfigUtils.java +++ b/src/main/java/com/wenshuo/agent/ConfigUtils.java @@ -7,6 +7,9 @@ import java.util.Properties; import java.util.Set; +import com.wenshuo.agent.applog.AppLogFactory; +import com.wenshuo.agent.applog.IAppLog; + /** * 获取配置信息
* ConfigUtils @@ -24,6 +27,8 @@ public class ConfigUtils { private static Set excludeClassRegexs; + private static IAppLog log = AppLogFactory.getAppLog(ConfigUtils.class); + private ConfigUtils() { super(); } @@ -49,7 +54,7 @@ private static Properties getProperties(String propertiesFileName) { input = ConfigUtils.class.getClassLoader().getResourceAsStream("agent.properties"); properties.load(input); } catch (Exception e) { - System.err.println("未找到默认配置"); + log.warn("未找到默认配置"); } finally { AgentUtils.closeQuietly(input); } @@ -58,7 +63,7 @@ private static Properties getProperties(String propertiesFileName) { input = new FileInputStream(propertiesFileName); properties.load(input); } catch (Exception e) { - System.err.println(e); + log.error("解析配置文件出错:" + propertiesFileName, e); } finally { AgentUtils.closeQuietly(input); } @@ -158,4 +163,5 @@ public static boolean isUsingNanoTime() { String value = getProperty(ConfigConsts.LOG_TIME_NANO); return "true".equalsIgnoreCase(value); } + } diff --git a/src/main/java/com/wenshuo/agent/applog/AppLogFactory.java b/src/main/java/com/wenshuo/agent/applog/AppLogFactory.java new file mode 100644 index 0000000..4283064 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/applog/AppLogFactory.java @@ -0,0 +1,52 @@ +/** + * @projectName javaagent + * @package com.wenshuo.agent.applog + * @className com.wenshuo.agent.applog.IAppLogFactory + * @copyright Copyright 2023 Thunisoft, Inc All rights reserved. + */ + +package com.wenshuo.agent.applog; + +/** + * AppLogFactory + * + * @author dingjsh + * @description javaagent日志工厂类 + * @date 2023-10-16 11:49 + * @since 2.1.3 + */ +public class AppLogFactory { + + private static final String SLF4J_FACTORY_CLASS_NAME = "org.slf4j.LoggerFactory"; + + private static final String COMMONS_LOGGING_FACTORY_CLASS_NAME = "org.apache.commons.logging.LogFactory"; + + /** + * 如果工程中有slf4j,则采用slf4j输出日志,如果有commons-logging,则采用commons-logging,否则用console + * @param clazz + * @return javaagent日志对象 + */ + public static IAppLog getAppLog(Class clazz) { + if (isClassPresent(SLF4J_FACTORY_CLASS_NAME, Thread.currentThread().getContextClassLoader())) { + return new Slf4jAppLog(clazz); + } else if (isClassPresent(COMMONS_LOGGING_FACTORY_CLASS_NAME, Thread.currentThread().getContextClassLoader())) { + return new CommonsLoggingAppLog(clazz); + } else { + return new ConsoleAppLog(); + } + } + + protected static boolean isClassPresent(String className, ClassLoader classLoader) { + try { + resolve(className, classLoader); + return true; + } catch (Throwable throwable) { + return false; + } + } + + protected static Class resolve(String className, ClassLoader classLoader) throws ClassNotFoundException { + return classLoader != null ? Class.forName(className, false, classLoader) : Class.forName(className); + } + +} \ No newline at end of file diff --git a/src/main/java/com/wenshuo/agent/applog/CommonsLoggingAppLog.java b/src/main/java/com/wenshuo/agent/applog/CommonsLoggingAppLog.java new file mode 100644 index 0000000..f832c0d --- /dev/null +++ b/src/main/java/com/wenshuo/agent/applog/CommonsLoggingAppLog.java @@ -0,0 +1,50 @@ +/** + * @projectName javaagent + * @package com.wenshuo.agent.applog + * @className com.wenshuo.agent.applog.CommonsLoggingAppLog + * @copyright Copyright 2023 Thunisoft, Inc All rights reserved. + */ + +package com.wenshuo.agent.applog; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * CommonsLoggingAppLog + * + * @author dingjsh + * @description + * @date 2023-10-16 17:35 + * @since 2.1.3 + */ +class CommonsLoggingAppLog implements IAppLog { + + private Log log; + + public CommonsLoggingAppLog(Class clazz) { + log = LogFactory.getLog(clazz); + } + + @Override + public void info(String message) { + log.info(message); + + } + + @Override + public void warn(String message) { + log.warn(message); + } + + @Override + public void error(String message) { + log.error(message); + } + + @Override + public void error(String message, Throwable t) { + log.error(message, t); + } + +} \ No newline at end of file diff --git a/src/main/java/com/wenshuo/agent/applog/ConsoleAppLog.java b/src/main/java/com/wenshuo/agent/applog/ConsoleAppLog.java new file mode 100644 index 0000000..d9011c8 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/applog/ConsoleAppLog.java @@ -0,0 +1,40 @@ +/** + * @projectName javaagent + * @package com.wenshuo.agent.applog + * @className com.wenshuo.agent.applog.ConsoleAppLog + * @copyright Copyright 2023 Thunisoft, Inc All rights reserved. + */ + +package com.wenshuo.agent.applog; + +/** + * ConsoleAppLog + * + * @author dingjsh + * @description + * @date 2023-10-16 17:37 + * @since 2.1.3 + */ +class ConsoleAppLog implements IAppLog { + + @Override + public void info(String message) { + System.out.println("[info ] " + message); + } + + @Override + public void warn(String message) { + System.out.println("[warn ] " + message); + } + + @Override + public void error(String message) { + System.err.println("[error ] " + message); + } + + @Override + public void error(String message, Throwable t) { + System.err.println("[error ] " + message); + } + +} \ No newline at end of file diff --git a/src/main/java/com/wenshuo/agent/applog/IAppLog.java b/src/main/java/com/wenshuo/agent/applog/IAppLog.java new file mode 100644 index 0000000..2aa9821 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/applog/IAppLog.java @@ -0,0 +1,28 @@ +/** + * @projectName javaagent + * @package com.wenshuo.agent.applog + * @className com.wenshuo.agent.applog.IAppLog + * @copyright Copyright 2023 Thunisoft, Inc All rights reserved. + */ + +package com.wenshuo.agent.applog; + +/** + * IAppLog + * + * @author dingjsh + * @description javaagent日志接口 + * @date 2023-10-16 11:46 + * @since 2.1.3 + */ +public interface IAppLog { + + void info(String message); + + void warn(String message); + + void error(String message); + + void error(String message, Throwable t); + +} \ No newline at end of file diff --git a/src/main/java/com/wenshuo/agent/applog/Slf4jAppLog.java b/src/main/java/com/wenshuo/agent/applog/Slf4jAppLog.java new file mode 100644 index 0000000..ed60c66 --- /dev/null +++ b/src/main/java/com/wenshuo/agent/applog/Slf4jAppLog.java @@ -0,0 +1,50 @@ +/** + * @projectName javaagent + * @package com.wenshuo.agent.applog + * @className com.wenshuo.agent.applog.Slf4jAppLog + * @copyright Copyright 2023 Thunisoft, Inc All rights reserved. + */ + +package com.wenshuo.agent.applog; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Slf4jAppLog + * + * @author dingjsh + * @description + * @date 2023-10-16 16:58 + * @since 2.1.3 + */ +class Slf4jAppLog implements IAppLog { + + private Logger log; + + public Slf4jAppLog(Class clazz) { + log = LoggerFactory.getLogger(clazz); + } + + @Override + public void info(String message) { + log.info(message); + + } + + @Override + public void warn(String message) { + log.warn(message); + } + + @Override + public void error(String message) { + log.error(message); + } + + @Override + public void error(String message, Throwable t) { + log.error(message, t); + } + +} \ No newline at end of file diff --git a/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java b/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java index c1f9678..624d8c1 100644 --- a/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java +++ b/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java @@ -2,6 +2,7 @@ * @(#)ExecuteLogUtils.java 2015-7-27 下午05:57:01 javaagent Copyright 2015 wenshuo, Inc. All rights reserved. wenshuo * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ + package com.wenshuo.agent.log; import java.io.BufferedWriter; @@ -24,6 +25,8 @@ import com.wenshuo.agent.AgentUtils; import com.wenshuo.agent.ConfigUtils; import com.wenshuo.agent.NamedThreadFactory; +import com.wenshuo.agent.applog.AppLogFactory; +import com.wenshuo.agent.applog.IAppLog; /** * ExecuteLogUtils @@ -57,6 +60,8 @@ public class ExecuteLogUtils { private static volatile boolean isInitialized = false; + private static final IAppLog log = AppLogFactory.getAppLog(ExecuteLogUtils.class); + private ExecuteLogUtils() { super(); } @@ -76,7 +81,7 @@ public static synchronized void init() { logAvgExecuteTime = ConfigUtils.isLogAvgExecuteTime(); isUsingNanoTime = ConfigUtils.isUsingNanoTime(); if (AgentUtils.isBlank(logFileName)) { - System.err.println("日志文件名为空"); + log.error("日志文件名为空"); throw new RuntimeException("日志文件名为空"); } setNextDateStartTimeMillis(); @@ -136,7 +141,7 @@ private static void logExecuteCounter(String className, String methodName, long ConcurrentHashMap methodCounterMap = getOrCreateClassExecutesMapping(className); long[] counter = methodCounterMap.get(methodName); if (null == counter) { - methodCounterMap.put(methodName, new long[]{1, executeTime}); + methodCounterMap.put(methodName, new long[] {1, executeTime}); } else { counter[0]++; counter[1] = executeTime + counter[1]; @@ -179,7 +184,7 @@ private static void writeLog(String logValue, boolean newLine, long currTimeMill counterLogWriter.newLine(); } } catch (IOException e) { - System.err.println(e); + log.error(e.getMessage(), e); } } @@ -188,7 +193,7 @@ private static void flushLogAndClose() { try { counterLogWriter.flush(); } catch (IOException e) { - System.err.println(e); + log.error(e.getMessage(), e); } finally { AgentUtils.closeQuietly(counterLogWriter); counterLogWriter = null; @@ -224,7 +229,7 @@ private static void initWriter() { counterLogWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile, true), ENCODING), BUFFER_SIZE); } catch (IOException e) { - System.err.println(e); + log.error(e.getMessage(), e); throw new RuntimeException("无法初始化【" + logFileName + "】,建议您检查磁盘空间,或者手动删除该日志文件"); } } @@ -254,7 +259,7 @@ private static File getLogFile(String logFileName) throws IOException { AgentUtils.forceMkdir(dirFile); boolean created = logFile.createNewFile(); if (!created) { - System.err.println("【" + logFileName + "】创建失败"); + log.error("【" + logFileName + "】创建失败"); throw new RuntimeException("【" + logFileName + "】创建失败"); } } @@ -276,7 +281,7 @@ private static Date date2StartTime(Date date) { try { startTime = sdf.parse(dateStr + " 00:00:00"); } catch (ParseException e) { - System.err.println(e); + log.error(e.getMessage(), e); } return startTime; } @@ -313,4 +318,5 @@ private static long removeJSONArrayEndBracket() throws IOException { f.close(); return length; } + } \ No newline at end of file diff --git a/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java b/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java index 3a08620..09e4f47 100644 --- a/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java +++ b/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java @@ -4,27 +4,28 @@ * Copyright 2015 wenshuo, Inc. All rights reserved. * wenshuo PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ + package com.wenshuo.agent.log; +import com.wenshuo.agent.applog.AppLogFactory; +import com.wenshuo.agent.applog.IAppLog; + /** * OutputLogRunnable - * + * * @author dingjsh * @time 2015-7-28下午03:27:20 */ public class OutputLogRunnable implements Runnable { - /* - * (non-Javadoc) - * - * @see java.lang.Runnable#run() - */ + private static IAppLog log = AppLogFactory.getAppLog(OutputLogRunnable.class); + @Override public void run() { - try{ + try { ExecuteLogUtils.outputCounterLog(); - }catch(Exception e){ - System.err.println(e); + } catch (Exception e) { + log.error(e.getMessage(), e); } } diff --git a/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java b/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java index 545b231..6602f06 100644 --- a/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java +++ b/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java @@ -1,7 +1,16 @@ package com.wenshuo.agent.transformer; +import java.io.IOException; +import java.lang.instrument.ClassFileTransformer; +import java.security.ProtectionDomain; +import java.util.Collections; +import java.util.Set; +import java.util.regex.Pattern; + import com.wenshuo.agent.ConfigUtils; import com.wenshuo.agent.PojoDetector; +import com.wenshuo.agent.applog.AppLogFactory; +import com.wenshuo.agent.applog.IAppLog; import com.wenshuo.agent.javassist.CannotCompileException; import com.wenshuo.agent.javassist.ClassPool; import com.wenshuo.agent.javassist.CtClass; @@ -9,16 +18,10 @@ import com.wenshuo.agent.javassist.LoaderClassPath; import com.wenshuo.agent.javassist.Modifier; import com.wenshuo.agent.javassist.NotFoundException; -import java.io.IOException; -import java.lang.instrument.ClassFileTransformer; -import java.security.ProtectionDomain; -import java.util.Collections; -import java.util.Set; -import java.util.regex.Pattern; /** * AgentLogClassFileTransformer 类增强,增加agent日志 - * + * * @author dingjsh * @time 2015-7-24上午09:53:44 */ @@ -28,9 +31,10 @@ public class AgentLogClassFileTransformer implements ClassFileTransformer { private static final String AGENT_PACKAGE_NAME = "com.wenshuo.agent"; + private static IAppLog log = AppLogFactory.getAppLog(AgentLogClassFileTransformer.class); public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, - ProtectionDomain protectionDomain, byte[] classfileBuffer) { + ProtectionDomain protectionDomain, byte[] classfileBuffer) { byte[] byteCode = classfileBuffer; className = className.replace('/', '.'); if (!isNeedLogExecuteInfo(className)) { @@ -55,7 +59,7 @@ private byte[] aopLog(ClassLoader loader, String className, byte[] byteCode) { } byteCode = aopLog(cc, className, byteCode); } catch (Exception ex) { - System.err.println(ex); + log.error(ex.getMessage(), ex); } return byteCode; } @@ -91,19 +95,20 @@ private void aopLog(String className, CtMethod m) throws CannotCompileException boolean isMethodStatic = Modifier.isStatic(m.getModifiers()); String aopClassName = isMethodStatic ? "\"" + className + "\"" : "this.getClass().getName()"; final String timeMethodStr - = ConfigUtils.isUsingNanoTime() ? "java.lang.System.nanoTime()" : "java.lang.System.currentTimeMillis()"; + = ConfigUtils.isUsingNanoTime() ? "java.lang.System.nanoTime()" : "java.lang.System.currentTimeMillis" + + "()"; // 避免变量名重复 m.addLocalVariable("dingjsh_javaagent_elapsedTime", CtClass.longType); m.insertBefore("dingjsh_javaagent_elapsedTime = " + timeMethodStr + ";"); m.insertAfter("dingjsh_javaagent_elapsedTime = " + timeMethodStr + " - dingjsh_javaagent_elapsedTime;"); m.insertAfter(LOG_UTILS + ".log(" + aopClassName + ",\"" + m.getName() - + "\",(long)dingjsh_javaagent_elapsedTime" + ");"); + + "\",(long)dingjsh_javaagent_elapsedTime" + ");"); } /** * 是否需要记录执行信息 - * + * * @param className 类名 * @return 是否需要记录执行信息 * @author dingjsh @@ -152,4 +157,5 @@ private boolean isNeedLogExecuteInfo(String className) { } return isNeeded; } + } From cf8b05b884f9e04d7d6d35115e39ecde4ce31443 Mon Sep 17 00:00:00 2001 From: fanzhongwei Date: Fri, 6 Sep 2024 11:39:15 +0800 Subject: [PATCH 20/25] =?UTF-8?q?feat=EF=BC=9A=E6=B7=BB=E5=8A=A0=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E6=89=A7=E8=A1=8C=E6=97=B6=E9=97=B4=E7=99=BE=E5=88=86?= =?UTF-8?q?=E6=AF=94=E7=BB=9F=E8=AE=A1=EF=BC=8C=E4=B8=8EJMeter=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E6=8A=A5=E5=91=8A=E7=BB=9F=E8=AE=A1=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + README.md | 4 + pom.xml | 32 ++++- .../java/com/wenshuo/agent/AgentUtils.java | 2 +- .../java/com/wenshuo/agent/ConfigConsts.java | 6 + .../java/com/wenshuo/agent/ConfigUtils.java | 24 ++++ .../wenshuo/agent/log/ExecuteLogUtils.java | 68 +++++++--- .../agent/log/MethodExecuteJSONformatter.java | 32 ++++- src/main/resources/agent.properties | 4 + .../com/wenshuo/agent/test/TestCtMethod.java | 128 +++++++++++++++++- 10 files changed, 271 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 3a8beb1..5a452fe 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ target/maven-archiver/pom.properties /.settings /.idea *.log +dependency-reduced-pom.xml diff --git a/README.md b/README.md index 84f08d7..072292e 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ agent.exclude.class.regex= agent.log.avg.execute.time=false # 记录方法的耗时时是采用nanoTime,还是currentTimeMillis,nanoTime更准确,但是会耗时一些 agent.log.nano=true +# 是否统计方法执行时间百分比,同JMeter性能测试百分比计算方式,如果开启默认会统计最大值、最小值 +agent.log.stat.execute.time=false +# 方法执行时间统计百分比(agent.log.stat.execute.time=true时有效),多选范围[0, 1],例如:0.5,0.9,0.95,0.99 +agent.log.stat.execute.time.pct=0.5,0.9,0.95,0.99 ``` ## agent日志分析器使用 diff --git a/pom.xml b/pom.xml index 7a93d28..5854424 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.wenshuo javaagent - 2.1.2 + 2.1.3 jar javaagent @@ -45,6 +45,31 @@ + + org.apache.maven.plugins + maven-shade-plugin + 3.1.1 + + + package + + shade + + + + + com.tdunning:t-digest + + + + + ccom.wenshuo.agent.Agent + + + + + + org.apache.maven.plugins maven-compiler-plugin @@ -86,5 +111,10 @@ compile true + + com.tdunning + t-digest + 3.3 + diff --git a/src/main/java/com/wenshuo/agent/AgentUtils.java b/src/main/java/com/wenshuo/agent/AgentUtils.java index e133898..6c6a871 100644 --- a/src/main/java/com/wenshuo/agent/AgentUtils.java +++ b/src/main/java/com/wenshuo/agent/AgentUtils.java @@ -69,7 +69,7 @@ public static boolean isBlank(String str) { * whitespace * @since 2.0 */ - static boolean isNotBlank(String str) { + public static boolean isNotBlank(String str) { return !isBlank(str); } diff --git a/src/main/java/com/wenshuo/agent/ConfigConsts.java b/src/main/java/com/wenshuo/agent/ConfigConsts.java index 6edd2c4..4bac970 100644 --- a/src/main/java/com/wenshuo/agent/ConfigConsts.java +++ b/src/main/java/com/wenshuo/agent/ConfigConsts.java @@ -34,6 +34,12 @@ public interface ConfigConsts { String LOG_TIME_NANO = "agent.log.nano"; + /** 是否统计方法执行时间百分比,同JMeter性能测试百分比计算方式,如果开启默认会统计最大值、最小值*/ + String LOG_STAT_EXECUTE_TIME = "agent.log.stat.execute.time"; + + /** 方法执行时间统计百分比(agent.log.stat.execute.time=true时有效),多选范围[0, 1],例如:0.5,0.9,0.95,0.99 */ + String LOG_STAT_EXECUTE_TIME_PCT = "agent.log.stat.execute.time.pct"; + int DEFAULT_LOG_INTERVAL = 600; } diff --git a/src/main/java/com/wenshuo/agent/ConfigUtils.java b/src/main/java/com/wenshuo/agent/ConfigUtils.java index e086fc2..73af9dc 100644 --- a/src/main/java/com/wenshuo/agent/ConfigUtils.java +++ b/src/main/java/com/wenshuo/agent/ConfigUtils.java @@ -164,4 +164,28 @@ public static boolean isUsingNanoTime() { return "true".equalsIgnoreCase(value); } + /** + * ConfigUtils + * + * @author fanzhongwei + * @return boolean 是否统计方法执行时间百分比,同JMeter性能测试百分比计算方式 + * @date 2024/9/5 下午7:12 + **/ + public static boolean isLogStatExecuteTime() { + String value = getProperty(ConfigConsts.LOG_STAT_EXECUTE_TIME); + return "true".equalsIgnoreCase(value); + } + + /** + * ConfigUtils + * + * @author fanzhongwei + * @return boolean 方法执行时间统计百分比(agent.log.stat.execute.time=true时有效),多选范围[0, 1],例如:0.5,0.9,0.95,0.99 + * @date 2024/9/5 下午7:13 + **/ + public static String getLogStatExecuteTimePct() { + String value = getProperty(ConfigConsts.LOG_STAT_EXECUTE_TIME_PCT); + return value; + } + } diff --git a/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java b/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java index 624d8c1..8ac2305 100644 --- a/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java +++ b/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java @@ -22,6 +22,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import com.tdunning.math.stats.MergingDigest; import com.wenshuo.agent.AgentUtils; import com.wenshuo.agent.ConfigUtils; import com.wenshuo.agent.NamedThreadFactory; @@ -46,7 +47,8 @@ public class ExecuteLogUtils { private static ScheduledThreadPoolExecutor counterLogExecutor; - private static ConcurrentHashMap> executeCounterMap; + // Map > + private static ConcurrentHashMap> executeCounterMap; private static final Object executeCounterLock = new Object(); @@ -58,6 +60,10 @@ public class ExecuteLogUtils { private static boolean logAvgExecuteTime = false; + private static boolean logStatExecuteTime = false; + + private static double[] logStatExecuteTimePct = new double[] {0.5, 0.9, 0.95, 0.99}; + private static volatile boolean isInitialized = false; private static final IAppLog log = AppLogFactory.getAppLog(ExecuteLogUtils.class); @@ -80,13 +86,22 @@ public static synchronized void init() { int interval = ConfigUtils.getLogInterval(); logAvgExecuteTime = ConfigUtils.isLogAvgExecuteTime(); isUsingNanoTime = ConfigUtils.isUsingNanoTime(); + logStatExecuteTime = ConfigUtils.isLogStatExecuteTime(); + String statPct = ConfigUtils.getLogStatExecuteTimePct(); + if (AgentUtils.isNotBlank(statPct)) { + String[] pctArr = statPct.split(","); + logStatExecuteTimePct = new double[pctArr.length]; + for (int i = 0; i < pctArr.length; i++) { + logStatExecuteTimePct[i] = Double.parseDouble(pctArr[i]); + } + } if (AgentUtils.isBlank(logFileName)) { log.error("日志文件名为空"); throw new RuntimeException("日志文件名为空"); } setNextDateStartTimeMillis(); initWriter(); - executeCounterMap = new ConcurrentHashMap>(); + executeCounterMap = new ConcurrentHashMap>(); startTimeMillis = System.currentTimeMillis(); counterLogExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("pool-thread-agent-log", true)); counterLogExecutor.scheduleWithFixedDelay(new OutputLogRunnable(), interval, interval, TimeUnit.SECONDS); @@ -94,7 +109,11 @@ public static synchronized void init() { } public static void log(String className, String methodName, long executeTime) { - logExecuteCounter(className, methodName, executeTime); + try { + logExecuteCounter(className, methodName, executeTime); + } catch (Exception e) { + log.error("记录方法执行日志失败", e); + } } /** @@ -104,8 +123,8 @@ public static void log(String className, String methodName, long executeTime) { * @time 2015-7-28下午01:35:25 */ synchronized static void outputCounterLog() throws IOException { - ConcurrentHashMap> executeCounterMapInner = executeCounterMap; - executeCounterMap = new ConcurrentHashMap>(); + ConcurrentHashMap> executeCounterMapInner = executeCounterMap; + executeCounterMap = new ConcurrentHashMap>(); String startTime = formatTimeMillis(startTimeMillis); String endTime = formatTimeMillis(System.currentTimeMillis()); long byteLength = removeJSONArrayEndBracket(); @@ -113,18 +132,18 @@ synchronized static void outputCounterLog() throws IOException { writeLog("[", true, startTimeMillis); } - Set>> entrySet = executeCounterMapInner.entrySet(); - Iterator>> ite = entrySet.iterator(); + Set>> entrySet = executeCounterMapInner.entrySet(); + Iterator>> ite = entrySet.iterator(); int length = entrySet.size(); if (length > 0 && byteLength > 10) { // 说明文件不只是[],json数组中已经有内容 writeLog(",", startTimeMillis); } for (int index = 0; ite.hasNext(); index++) { - Map.Entry> entry = ite.next(); + Map.Entry> entry = ite.next(); String className = entry.getKey(); - Map method2ExecuteMap = entry.getValue(); + Map method2ExecuteMap = entry.getValue(); String methodExecuteJson = MethodExecuteJSONformatter.getMethodExecuteJSON(className, method2ExecuteMap, - startTime, endTime, isUsingNanoTime, logAvgExecuteTime); + startTime, endTime, isUsingNanoTime, logAvgExecuteTime, logStatExecuteTime, logStatExecuteTimePct); writeLog(methodExecuteJson, startTimeMillis); if (index < length - 1) { writeLog(",", true, startTimeMillis); @@ -138,13 +157,26 @@ synchronized static void outputCounterLog() throws IOException { private static void logExecuteCounter(String className, String methodName, long executeTime) { // dingjs modified in 20210825 原来是用锁和AtomicLong来避免线程安全问题,现在去掉这些实现。原因是此处线程安全问题顶多导致 // 数据有一些误差,这个误差对于agent数据监控并没有什么影响,牺牲一定的准确性带来性能的提升是有必要的 - ConcurrentHashMap methodCounterMap = getOrCreateClassExecutesMapping(className); - long[] counter = methodCounterMap.get(methodName); + ConcurrentHashMap methodCounterMap = getOrCreateClassExecutesMapping(className); + Object[] counter = methodCounterMap.get(methodName); if (null == counter) { - methodCounterMap.put(methodName, new long[] {1, executeTime}); + MergingDigest md = null; + if (logStatExecuteTime) { + int compression = 150; + int factor = 5; + md = new MergingDigest(compression, (factor + 1) * compression, compression); + } + methodCounterMap.put(methodName, new Object[] {1L, executeTime, md}); } else { - counter[0]++; - counter[1] = executeTime + counter[1]; + counter[0] = (Long) counter[0] + 1L; + counter[1] = executeTime + (Long) counter[1]; + if (logStatExecuteTime) { + MergingDigest md = (MergingDigest) counter[2]; + // MergingDigest需要线程安全 + synchronized (md) { + md.add(executeTime); + } + } } } @@ -158,13 +190,13 @@ private static void logExecuteCounter(String className, String methodName, long * @date 2018年5月25日 下午4:40:33 * @version 1.2.0 */ - private static ConcurrentHashMap getOrCreateClassExecutesMapping(String className) { - ConcurrentHashMap methodCounterMap = executeCounterMap.get(className); + private static ConcurrentHashMap getOrCreateClassExecutesMapping(String className) { + ConcurrentHashMap methodCounterMap = executeCounterMap.get(className); if (null == methodCounterMap) { synchronized (executeCounterLock) { methodCounterMap = executeCounterMap.get(className); if (null == methodCounterMap) { - methodCounterMap = new ConcurrentHashMap(); + methodCounterMap = new ConcurrentHashMap(); executeCounterMap.put(className, methodCounterMap); } } diff --git a/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java b/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java index 13e1454..fa28d2e 100644 --- a/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java +++ b/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java @@ -1,5 +1,7 @@ package com.wenshuo.agent.log; +import com.tdunning.math.stats.MergingDigest; + import java.util.Map; @@ -17,7 +19,7 @@ class MethodExecuteJSONformatter { * MethodExecuteJSONformatter * * @param className 类名 - * @param method2ExecuteMap Map<方法名, Long[]> 数组第一个是执行次数,第二个是执行时间 + * @param method2ExecuteMap Map<方法名, Object[]> 数组第一个是执行次数,第二个是执行时间,第三个是方法执行时间统计(MergingDigest, 根据配置可选) * @param startTime 统计周期的开始时间,格式为 yyyy-MM-dd HH:mm:ss * @param endTime 统计周期的结束时间,格式为 yyyy-MM-dd HH:mm:ss * @return 格式化后的json string @@ -26,19 +28,21 @@ class MethodExecuteJSONformatter { * @date 2018年8月22日 下午8:49:18 * @version 2.0.0 */ - static String getMethodExecuteJSON(String className, Map method2ExecuteMap, - String startTime, String endTime, boolean isUsingNanoTime, boolean logAvgExecuteTime) { + static String getMethodExecuteJSON(String className, Map method2ExecuteMap, + String startTime, String endTime, boolean isUsingNanoTime, boolean logAvgExecuteTime, + boolean logStatExecuteTime, double[] logStatExecuteTimePct) { StringBuilder json = new StringBuilder("{"); appendString(json, "class", className).append(","); appendString(json, "start", startTime).append(","); appendString(json, "end", endTime).append(","); appendKey(json, "methods").append("["); - for (Map.Entry methodEntry : method2ExecuteMap.entrySet()) { + for (Map.Entry methodEntry : method2ExecuteMap.entrySet()) { String methodName = methodEntry.getKey(); - long[] executeCounter = methodEntry.getValue(); - long counter = executeCounter[0]; + Object[] executeCounter = methodEntry.getValue(); + Long counter = (Long) executeCounter[0]; + Long totalTime = (Long) executeCounter[1]; long timeInMillis - = isUsingNanoTime ? executeCounter[1] / 1000000 : executeCounter[1]; + = isUsingNanoTime ? totalTime / 1000000 : totalTime; json.append("{"); appendString(json, "name", methodName).append(","); appendLong(json, "counter", counter).append(","); @@ -47,6 +51,20 @@ static String getMethodExecuteJSON(String className, Map method2 json.append(","); appendLong(json, "avg", timeInMillis / counter); } + if (logStatExecuteTime && counter > 0) { + MergingDigest md = (MergingDigest)executeCounter[2]; + json.append(","); + appendLong(json, "min", (long) md.getMin()); + + for (int i = 0; i < logStatExecuteTimePct.length; i++) { + double pct = logStatExecuteTimePct[i]; + json.append(","); + appendLong(json, "th" + (int)(pct * 100), (long) md.quantile(pct)); + } + + json.append(","); + appendLong(json, "max", (long) md.getMax()); + } json.append("},"); } if (json.charAt(json.length() - 1) == ',') { diff --git a/src/main/resources/agent.properties b/src/main/resources/agent.properties index 2117cd7..d5cb9f9 100644 --- a/src/main/resources/agent.properties +++ b/src/main/resources/agent.properties @@ -14,3 +14,7 @@ agent.log.avg.execute.time=false agent.exclude.class.regex.default=.*EnhancerByCGLIB.*;.*FastClassByCGLIB.*;.*EnhancerBySpringCGLIB.*;.*FastClassBySpringCGLIB.* #\u8bb0\u5f55\u65b9\u6cd5\u7684\u8017\u65f6\u65f6\u662f\u91c7\u7528nanoTime,\u8fd8\u662fcurrentTimeMillis,nanoTime\u66f4\u51c6\u786e\uff0c\u4f46\u662f\u4f1a\u8017\u65f6\u4e00\u4e9b agent.log.nano=true +#\u662f\u5426\u7edf\u8ba1\u65b9\u6cd5\u6267\u884c\u65f6\u95f4\u767e\u5206\u6bd4\uff0c\u540cJMeter\u6027\u80fd\u6d4b\u8bd5\u767e\u5206\u6bd4\u8ba1\u7b97\u65b9\u5f0f\uff0c\u5982\u679c\u5f00\u542f\u9ed8\u8ba4\u4f1a\u7edf\u8ba1\u6700\u5927\u503c\u3001\u6700\u5c0f\u503c +agent.log.stat.execute.time=false +#\u65b9\u6cd5\u6267\u884c\u65f6\u95f4\u7edf\u8ba1\u767e\u5206\u6bd4\uff08agent.log.stat.execute.time=true\u65f6\u6709\u6548\uff09\uff0c\u591a\u9009\u8303\u56f4[0, 1]\uff0c\u4f8b\u5982\uff1a0.5,0.9,0.95,0.99 +agent.log.stat.execute.time.pct=0.5,0.9,0.95,0.99 \ No newline at end of file diff --git a/src/test/java/com/wenshuo/agent/test/TestCtMethod.java b/src/test/java/com/wenshuo/agent/test/TestCtMethod.java index ecca591..66ec31a 100644 --- a/src/test/java/com/wenshuo/agent/test/TestCtMethod.java +++ b/src/test/java/com/wenshuo/agent/test/TestCtMethod.java @@ -1,9 +1,17 @@ package com.wenshuo.agent.test; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Random; +import java.util.concurrent.*; +import com.tdunning.math.stats.Centroid; +import com.tdunning.math.stats.Dist; +import com.tdunning.math.stats.MergingDigest; +import com.tdunning.math.stats.ScaleFunction; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import com.wenshuo.agent.javassist.ClassPool; @@ -44,7 +52,121 @@ public static void publicStatic(){ static void defaultStatic(){ System.out.println("I'm default and static"); } - - - + + + /** + * 100个线程并发测试,每个线程往100个不同的Digest中添加5000000个随机数据[0, 100],并计算耗时
+ * 内存限制:-Xms50m -Xmx50m + * + * 测试结论:从统计值来看,统计结果基本符合预期,并且耗时基本在800ms~1000ms,未发生内存溢出,初步评估可以加入到javaagent方法耗时统计中并且对性能影响在可控范围内
+ * 部分统计结果如下:
+ * MIN 10th pct 20th pct 25th pct 50th pct 75th pct 90th pct 95th pct 99th pct MAX 耗时
+ * ========== ========== ========== ========== ========== ========== ========== ========== ========== ========== ==========
+ * 0 10 20 25 50 75 90 95 99 100 942
+ * 0 10 20 25 50 75 90 95 99 100 869
+ * 0 10 20 25 50 75 90 95 99 100 939
+ * 0 10 20 25 50 75 90 95 99 100 895
+ * 0 10 20 25 50 75 90 95 99 100 942
+ * 0 10 20 25 50 75 90 95 99 100 845
+ * 0 10 20 25 50 75 90 95 99 100 887
+ * 0 10 20 25 50 75 90 95 99 100 898
+ * 0 10 20 25 50 75 90 95 99 100 911
+ * 0 10 20 25 50 75 90 95 99 100 815
+ * 0 10 20 25 50 75 90 95 99 100 762
+ * 0 10 20 25 50 75 90 95 99 100 893
+ * 0 10 20 25 50 75 90 95 99 100 933
+ * 0 10 20 25 50 75 90 95 99 100 866
+ * 0 10 20 25 50 75 90 95 99 100 863
+ * 0 10 20 25 50 75 90 95 99 100 920
+ * 0 10 20 25 50 75 90 95 99 100 830
+ * 0 10 20 25 50 75 90 95 99 100 870
+ * 0 10 20 25 50 75 90 95 100 100 913
+ * + * + * @return void + * @date 2024/9/5 下午6:41 + **/ + @Test + @Ignore + public void testMethodPctCounter() { + int compression = 150; + int factor = 5; + + final int M = 100; + final List mds = new ArrayList(M); + final long[] counts = new long[M]; + for (int i = 0; i < M; ++i) { + mds.add(new MergingDigest(compression, (factor + 1) * compression, compression)); + counts[i] = 0; + } + + // Fill all digests with random values (0~100). + final Random random = new Random(); + + ThreadPoolExecutor executorService = new ThreadPoolExecutor( + 100, + 100, + 0, + TimeUnit.MINUTES, + new ArrayBlockingQueue(1000), + new ThreadPoolExecutor.CallerRunsPolicy()); + for (int i = 0; i < 5000000; ++i) { + executorService.execute((new Runnable() { + @Override + public void run() { + for (int j = 0; j < M; ++j) { + MergingDigest md = mds.get(j); + synchronized (md) { + int data = random.nextInt(101); + long start = System.currentTimeMillis(); + md.add(data); + counts[j] = counts[j] + (System.currentTimeMillis() - start); + } + } + } + })); + } + while (true) { + if (executorService.getActiveCount() != 0) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else { + break; + } + } + executorService.shutdown(); + + // Output + double[] qArr = new double[]{0.1, 0.2, 0.25, 0.5, 0.75, 0.90, 0.95, 0.99}; + System.out.printf("%10s\t", "MIN"); + for (int i = 0 ; i < qArr.length; ++i) { + System.out.printf("%4sth pct\t", (int)(qArr[i] * 100)); + } + System.out.printf("%10s\t", "MAX"); + System.out.printf("%10s\t", "耗时"); + System.out.println(); + for (int i = 0 ; i < 11; ++i) { + System.out.print("==========\t"); + } + System.out.println(); + + + for (int i = 0; i < mds.size(); ++i) { + MergingDigest md = mds.get(i); + System.out.printf("%10.0f\t", md.getMin()); + long start = System.currentTimeMillis(); + for (double q : qArr) { + System.out.printf("%10.0f\t", md.quantile(q)); + } + counts[i] = counts[i] + (System.currentTimeMillis() - start); + System.out.printf("%10.0f\t", md.getMax()); + System.out.printf("%10d\t", counts[i]); + System.out.println(); + } + + } + } From c9dcb8487b292070912398a85bec4ef776e45aab Mon Sep 17 00:00:00 2001 From: fanzhongwei Date: Fri, 6 Sep 2024 18:38:28 +0800 Subject: [PATCH 21/25] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?agent=E6=97=A5=E5=BF=97=E5=88=86=E6=9E=90=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- agent-analyer/agent-analyzer.iml | 99 ++ agent-analyer/pom.xml | 138 +++ .../wenshuo/agent/analyzer/AgentAnalyzer.java | 18 + .../agent/analyzer/bean/AgentLogDO.java | 65 + .../agent/analyzer/bean/ClassBean.java | 39 + .../agent/analyzer/bean/MethodBean.java | 45 + .../configuration/MvcConfiguration.java | 104 ++ .../controller/CheckLogFileController.java | 187 +++ .../analyzer/controller/IndexController.java | 16 + .../agent/analyzer/dao/LogFileDao.java | 41 + .../analyzer/service/LogFileService.java | 150 +++ .../src/main/resources/application.yml | 40 + .../main/resources/static/html/result.html | 100 ++ .../src/main/resources/static/index.html | 634 ++++++++++ .../main/resources/static/js/ZeroClipboard.js | 458 +++++++ .../resources/static/js/ZeroClipboard.min.js | 9 + .../resources/static/js/ZeroClipboard.swf | Bin 0 -> 1963 bytes .../src/main/resources/static/js/amsverify.js | 154 +++ .../resources/static/js/auto-line-number.js | 100 ++ .../main/resources/static/js/diyfunction.js | 1081 +++++++++++++++++ .../resources/static/js/echarts.common.min.js | 22 + .../resources/static/js/jquery-1.7.2.min.js | 4 + .../resources/static/js/layui/css/layui.css | 2 + .../static/js/layui/css/layui.mobile.css | 2 + .../static/js/layui/css/modules/code.css | 2 + .../css/modules/laydate/default/laydate.css | 2 + .../css/modules/layer/default/icon-ext.png | Bin 0 -> 5911 bytes .../layui/css/modules/layer/default/icon.png | Bin 0 -> 11493 bytes .../layui/css/modules/layer/default/layer.css | 2 + .../css/modules/layer/default/loading-0.gif | Bin 0 -> 5793 bytes .../css/modules/layer/default/loading-1.gif | Bin 0 -> 701 bytes .../css/modules/layer/default/loading-2.gif | Bin 0 -> 1787 bytes .../static/js/layui/font/iconfont.eot | Bin 0 -> 40144 bytes .../static/js/layui/font/iconfont.svg | 468 +++++++ .../static/js/layui/font/iconfont.ttf | Bin 0 -> 39968 bytes .../static/js/layui/font/iconfont.woff | Bin 0 -> 26328 bytes .../static/js/layui/images/face/0.gif | Bin 0 -> 2689 bytes .../static/js/layui/images/face/1.gif | Bin 0 -> 5514 bytes .../static/js/layui/images/face/10.gif | Bin 0 -> 2797 bytes .../static/js/layui/images/face/11.gif | Bin 0 -> 4121 bytes .../static/js/layui/images/face/12.gif | Bin 0 -> 3361 bytes .../static/js/layui/images/face/13.gif | Bin 0 -> 7425 bytes .../static/js/layui/images/face/14.gif | Bin 0 -> 2375 bytes .../static/js/layui/images/face/15.gif | Bin 0 -> 1793 bytes .../static/js/layui/images/face/16.gif | Bin 0 -> 6721 bytes .../static/js/layui/images/face/17.gif | Bin 0 -> 4439 bytes .../static/js/layui/images/face/18.gif | Bin 0 -> 3017 bytes .../static/js/layui/images/face/19.gif | Bin 0 -> 3040 bytes .../static/js/layui/images/face/2.gif | Bin 0 -> 3222 bytes .../static/js/layui/images/face/20.gif | Bin 0 -> 5144 bytes .../static/js/layui/images/face/21.gif | Bin 0 -> 5191 bytes .../static/js/layui/images/face/22.gif | Bin 0 -> 9823 bytes .../static/js/layui/images/face/23.gif | Bin 0 -> 3792 bytes .../static/js/layui/images/face/24.gif | Bin 0 -> 8096 bytes .../static/js/layui/images/face/25.gif | Bin 0 -> 3127 bytes .../static/js/layui/images/face/26.gif | Bin 0 -> 3291 bytes .../static/js/layui/images/face/27.gif | Bin 0 -> 4377 bytes .../static/js/layui/images/face/28.gif | Bin 0 -> 2793 bytes .../static/js/layui/images/face/29.gif | Bin 0 -> 4854 bytes .../static/js/layui/images/face/3.gif | Bin 0 -> 4017 bytes .../static/js/layui/images/face/30.gif | Bin 0 -> 2555 bytes .../static/js/layui/images/face/31.gif | Bin 0 -> 2002 bytes .../static/js/layui/images/face/32.gif | Bin 0 -> 3481 bytes .../static/js/layui/images/face/33.gif | Bin 0 -> 2454 bytes .../static/js/layui/images/face/34.gif | Bin 0 -> 3700 bytes .../static/js/layui/images/face/35.gif | Bin 0 -> 1800 bytes .../static/js/layui/images/face/36.gif | Bin 0 -> 2331 bytes .../static/js/layui/images/face/37.gif | Bin 0 -> 1513 bytes .../static/js/layui/images/face/38.gif | Bin 0 -> 3615 bytes .../static/js/layui/images/face/39.gif | Bin 0 -> 6495 bytes .../static/js/layui/images/face/4.gif | Bin 0 -> 5689 bytes .../static/js/layui/images/face/40.gif | Bin 0 -> 3154 bytes .../static/js/layui/images/face/41.gif | Bin 0 -> 3644 bytes .../static/js/layui/images/face/42.gif | Bin 0 -> 5305 bytes .../static/js/layui/images/face/43.gif | Bin 0 -> 2674 bytes .../static/js/layui/images/face/44.gif | Bin 0 -> 4126 bytes .../static/js/layui/images/face/45.gif | Bin 0 -> 3417 bytes .../static/js/layui/images/face/46.gif | Bin 0 -> 3007 bytes .../static/js/layui/images/face/47.gif | Bin 0 -> 2333 bytes .../static/js/layui/images/face/48.gif | Bin 0 -> 2689 bytes .../static/js/layui/images/face/49.gif | Bin 0 -> 2315 bytes .../static/js/layui/images/face/5.gif | Bin 0 -> 4567 bytes .../static/js/layui/images/face/50.gif | Bin 0 -> 5866 bytes .../static/js/layui/images/face/51.gif | Bin 0 -> 2785 bytes .../static/js/layui/images/face/52.gif | Bin 0 -> 777 bytes .../static/js/layui/images/face/53.gif | Bin 0 -> 2127 bytes .../static/js/layui/images/face/54.gif | Bin 0 -> 2196 bytes .../static/js/layui/images/face/55.gif | Bin 0 -> 1971 bytes .../static/js/layui/images/face/56.gif | Bin 0 -> 2034 bytes .../static/js/layui/images/face/57.gif | Bin 0 -> 2705 bytes .../static/js/layui/images/face/58.gif | Bin 0 -> 2258 bytes .../static/js/layui/images/face/59.gif | Bin 0 -> 10311 bytes .../static/js/layui/images/face/6.gif | Bin 0 -> 2213 bytes .../static/js/layui/images/face/60.gif | Bin 0 -> 3245 bytes .../static/js/layui/images/face/61.gif | Bin 0 -> 2495 bytes .../static/js/layui/images/face/62.gif | Bin 0 -> 2017 bytes .../static/js/layui/images/face/63.gif | Bin 0 -> 5871 bytes .../static/js/layui/images/face/64.gif | Bin 0 -> 6448 bytes .../static/js/layui/images/face/65.gif | Bin 0 -> 3576 bytes .../static/js/layui/images/face/66.gif | Bin 0 -> 3029 bytes .../static/js/layui/images/face/67.gif | Bin 0 -> 2701 bytes .../static/js/layui/images/face/68.gif | Bin 0 -> 1424 bytes .../static/js/layui/images/face/69.gif | Bin 0 -> 2431 bytes .../static/js/layui/images/face/7.gif | Bin 0 -> 3398 bytes .../static/js/layui/images/face/70.gif | Bin 0 -> 4590 bytes .../static/js/layui/images/face/71.gif | Bin 0 -> 5304 bytes .../static/js/layui/images/face/8.gif | Bin 0 -> 4050 bytes .../static/js/layui/images/face/9.gif | Bin 0 -> 4221 bytes .../static/js/layui/lay/modules/carousel.js | 2 + .../static/js/layui/lay/modules/code.js | 2 + .../static/js/layui/lay/modules/element.js | 2 + .../static/js/layui/lay/modules/flow.js | 2 + .../static/js/layui/lay/modules/form.js | 2 + .../static/js/layui/lay/modules/jquery.js | 5 + .../static/js/layui/lay/modules/laydate.js | 2 + .../static/js/layui/lay/modules/layedit.js | 2 + .../static/js/layui/lay/modules/layer.js | 2 + .../static/js/layui/lay/modules/laypage.js | 2 + .../static/js/layui/lay/modules/laytpl.js | 2 + .../static/js/layui/lay/modules/mobile.js | 2 + .../static/js/layui/lay/modules/rate.js | 2 + .../static/js/layui/lay/modules/table.js | 2 + .../static/js/layui/lay/modules/tree.js | 2 + .../static/js/layui/lay/modules/upload.js | 2 + .../static/js/layui/lay/modules/util.js | 2 + .../resources/static/js/layui/layui.all.js | 5 + .../main/resources/static/js/layui/layui.js | 2 + .../main/resources/static/js/layui/upload.js | 2 + pom.xml => agent/pom.xml | 0 .../main/java/com/wenshuo/agent/Agent.java | 0 .../java/com/wenshuo/agent/AgentUtils.java | 0 .../java/com/wenshuo/agent/ConfigConsts.java | 0 .../java/com/wenshuo/agent/ConfigUtils.java | 0 .../com/wenshuo/agent/NamedThreadFactory.java | 0 .../java/com/wenshuo/agent/PojoDetector.java | 0 .../wenshuo/agent/applog/AppLogFactory.java | 0 .../agent/applog/CommonsLoggingAppLog.java | 0 .../wenshuo/agent/applog/ConsoleAppLog.java | 0 .../com/wenshuo/agent/applog/IAppLog.java | 0 .../com/wenshuo/agent/applog/Slf4jAppLog.java | 0 .../agent/javassist/ByteArrayClassPath.java | 0 .../javassist/CannotCompileException.java | 0 .../agent/javassist/ClassClassPath.java | 0 .../com/wenshuo/agent/javassist/ClassMap.java | 0 .../wenshuo/agent/javassist/ClassPath.java | 0 .../wenshuo/agent/javassist/ClassPool.java | 0 .../agent/javassist/ClassPoolTail.java | 0 .../agent/javassist/CodeConverter.java | 0 .../com/wenshuo/agent/javassist/CtArray.java | 0 .../wenshuo/agent/javassist/CtBehavior.java | 0 .../com/wenshuo/agent/javassist/CtClass.java | 0 .../wenshuo/agent/javassist/CtClassType.java | 0 .../agent/javassist/CtConstructor.java | 0 .../com/wenshuo/agent/javassist/CtField.java | 0 .../com/wenshuo/agent/javassist/CtMember.java | 0 .../com/wenshuo/agent/javassist/CtMethod.java | 0 .../wenshuo/agent/javassist/CtNewClass.java | 0 .../agent/javassist/CtNewConstructor.java | 0 .../wenshuo/agent/javassist/CtNewMethod.java | 0 .../agent/javassist/CtNewNestedClass.java | 0 .../javassist/CtNewWrappedConstructor.java | 0 .../agent/javassist/CtNewWrappedMethod.java | 0 .../agent/javassist/CtPrimitiveType.java | 0 .../com/wenshuo/agent/javassist/Loader.java | 0 .../agent/javassist/LoaderClassPath.java | 0 .../com/wenshuo/agent/javassist/Modifier.java | 0 .../agent/javassist/NotFoundException.java | 0 .../agent/javassist/SerialVersionUID.java | 0 .../wenshuo/agent/javassist/Translator.java | 0 .../wenshuo/agent/javassist/URLClassPath.java | 0 .../agent/javassist/bytecode/AccessFlag.java | 0 .../bytecode/AnnotationDefaultAttribute.java | 0 .../bytecode/AnnotationsAttribute.java | 0 .../javassist/bytecode/AttributeInfo.java | 0 .../agent/javassist/bytecode/BadBytecode.java | 0 .../bytecode/BootstrapMethodsAttribute.java | 0 .../agent/javassist/bytecode/ByteArray.java | 0 .../agent/javassist/bytecode/ByteStream.java | 0 .../agent/javassist/bytecode/Bytecode.java | 0 .../agent/javassist/bytecode/ClassFile.java | 0 .../javassist/bytecode/ClassFilePrinter.java | 0 .../javassist/bytecode/ClassFileWriter.java | 0 .../javassist/bytecode/CodeAnalyzer.java | 0 .../javassist/bytecode/CodeAttribute.java | 0 .../javassist/bytecode/CodeIterator.java | 0 .../agent/javassist/bytecode/ConstPool.java | 0 .../javassist/bytecode/ConstantAttribute.java | 0 .../bytecode/DeprecatedAttribute.java | 0 .../agent/javassist/bytecode/Descriptor.java | 0 .../bytecode/DuplicateMemberException.java | 0 .../bytecode/EnclosingMethodAttribute.java | 0 .../javassist/bytecode/ExceptionTable.java | 0 .../bytecode/ExceptionsAttribute.java | 0 .../agent/javassist/bytecode/FieldInfo.java | 0 .../bytecode/InnerClassesAttribute.java | 0 .../bytecode/InstructionPrinter.java | 0 .../bytecode/LineNumberAttribute.java | 0 .../bytecode/LocalVariableAttribute.java | 0 .../bytecode/LocalVariableTypeAttribute.java | 0 .../agent/javassist/bytecode/LongVector.java | 0 .../agent/javassist/bytecode/MethodInfo.java | 0 .../bytecode/MethodParametersAttribute.java | 0 .../agent/javassist/bytecode/Mnemonic.java | 0 .../agent/javassist/bytecode/Opcode.java | 0 .../ParameterAnnotationsAttribute.java | 0 .../bytecode/SignatureAttribute.java | 0 .../bytecode/SourceFileAttribute.java | 0 .../agent/javassist/bytecode/StackMap.java | 0 .../javassist/bytecode/StackMapTable.java | 0 .../bytecode/SyntheticAttribute.java | 0 .../bytecode/TypeAnnotationsAttribute.java | 0 .../javassist/bytecode/analysis/Analyzer.java | 0 .../bytecode/analysis/ControlFlow.java | 0 .../javassist/bytecode/analysis/Executor.java | 0 .../javassist/bytecode/analysis/Frame.java | 0 .../bytecode/analysis/FramePrinter.java | 0 .../javassist/bytecode/analysis/IntQueue.java | 0 .../bytecode/analysis/MultiArrayType.java | 0 .../bytecode/analysis/MultiType.java | 0 .../bytecode/analysis/Subroutine.java | 0 .../bytecode/analysis/SubroutineScanner.java | 0 .../javassist/bytecode/analysis/Type.java | 0 .../javassist/bytecode/analysis/Util.java | 0 .../javassist/bytecode/analysis/package.html | 0 .../bytecode/annotation/Annotation.java | 0 .../bytecode/annotation/AnnotationImpl.java | 0 .../annotation/AnnotationMemberValue.java | 0 .../annotation/AnnotationsWriter.java | 0 .../bytecode/annotation/ArrayMemberValue.java | 0 .../annotation/BooleanMemberValue.java | 0 .../bytecode/annotation/ByteMemberValue.java | 0 .../bytecode/annotation/CharMemberValue.java | 0 .../bytecode/annotation/ClassMemberValue.java | 0 .../annotation/DoubleMemberValue.java | 0 .../bytecode/annotation/EnumMemberValue.java | 0 .../bytecode/annotation/FloatMemberValue.java | 0 .../annotation/IntegerMemberValue.java | 0 .../bytecode/annotation/LongMemberValue.java | 0 .../bytecode/annotation/MemberValue.java | 0 .../annotation/MemberValueVisitor.java | 0 .../bytecode/annotation/NoSuchClassError.java | 0 .../bytecode/annotation/ShortMemberValue.java | 0 .../annotation/StringMemberValue.java | 0 .../annotation/TypeAnnotationsWriter.java | 0 .../bytecode/annotation/package.html | 0 .../agent/javassist/bytecode/package.html | 0 .../bytecode/stackmap/BasicBlock.java | 0 .../javassist/bytecode/stackmap/MapMaker.java | 0 .../javassist/bytecode/stackmap/Tracer.java | 0 .../javassist/bytecode/stackmap/TypeData.java | 0 .../javassist/bytecode/stackmap/TypeTag.java | 0 .../bytecode/stackmap/TypedBlock.java | 0 .../javassist/compiler/AccessorMaker.java | 0 .../agent/javassist/compiler/CodeGen.java | 0 .../javassist/compiler/CompileError.java | 0 .../agent/javassist/compiler/Javac.java | 0 .../agent/javassist/compiler/JvstCodeGen.java | 0 .../javassist/compiler/JvstTypeChecker.java | 0 .../javassist/compiler/KeywordTable.java | 0 .../wenshuo/agent/javassist/compiler/Lex.java | 0 .../javassist/compiler/MemberCodeGen.java | 0 .../javassist/compiler/MemberResolver.java | 0 .../javassist/compiler/NoFieldException.java | 0 .../agent/javassist/compiler/Parser.java | 0 .../javassist/compiler/ProceedHandler.java | 0 .../agent/javassist/compiler/SymbolTable.java | 0 .../agent/javassist/compiler/SyntaxError.java | 0 .../agent/javassist/compiler/TokenId.java | 0 .../agent/javassist/compiler/TypeChecker.java | 0 .../agent/javassist/compiler/ast/ASTList.java | 0 .../agent/javassist/compiler/ast/ASTree.java | 0 .../javassist/compiler/ast/ArrayInit.java | 0 .../javassist/compiler/ast/AssignExpr.java | 0 .../agent/javassist/compiler/ast/BinExpr.java | 0 .../javassist/compiler/ast/CallExpr.java | 0 .../javassist/compiler/ast/CastExpr.java | 0 .../javassist/compiler/ast/CondExpr.java | 0 .../javassist/compiler/ast/Declarator.java | 0 .../javassist/compiler/ast/DoubleConst.java | 0 .../agent/javassist/compiler/ast/Expr.java | 0 .../javassist/compiler/ast/FieldDecl.java | 0 .../compiler/ast/InstanceOfExpr.java | 0 .../javassist/compiler/ast/IntConst.java | 0 .../agent/javassist/compiler/ast/Keyword.java | 0 .../agent/javassist/compiler/ast/Member.java | 0 .../javassist/compiler/ast/MethodDecl.java | 0 .../agent/javassist/compiler/ast/NewExpr.java | 0 .../agent/javassist/compiler/ast/Pair.java | 0 .../agent/javassist/compiler/ast/Stmnt.java | 0 .../agent/javassist/compiler/ast/StringL.java | 0 .../agent/javassist/compiler/ast/Symbol.java | 0 .../javassist/compiler/ast/Variable.java | 0 .../agent/javassist/compiler/ast/Visitor.java | 0 .../convert/TransformAccessArrayField.java | 0 .../javassist/convert/TransformAfter.java | 0 .../javassist/convert/TransformBefore.java | 0 .../javassist/convert/TransformCall.java | 0 .../convert/TransformFieldAccess.java | 0 .../agent/javassist/convert/TransformNew.java | 0 .../javassist/convert/TransformNewClass.java | 0 .../javassist/convert/TransformReadField.java | 0 .../convert/TransformWriteField.java | 0 .../agent/javassist/convert/Transformer.java | 0 .../wenshuo/agent/javassist/expr/Cast.java | 0 .../agent/javassist/expr/ConstructorCall.java | 0 .../wenshuo/agent/javassist/expr/Expr.java | 0 .../agent/javassist/expr/ExprEditor.java | 0 .../agent/javassist/expr/FieldAccess.java | 0 .../wenshuo/agent/javassist/expr/Handler.java | 0 .../agent/javassist/expr/Instanceof.java | 0 .../agent/javassist/expr/MethodCall.java | 0 .../agent/javassist/expr/NewArray.java | 0 .../wenshuo/agent/javassist/expr/NewExpr.java | 0 .../wenshuo/agent/javassist/expr/package.html | 0 .../com/wenshuo/agent/javassist/package.html | 0 .../agent/javassist/runtime/Cflow.java | 0 .../wenshuo/agent/javassist/runtime/Desc.java | 0 .../agent/javassist/runtime/DotClass.java | 0 .../agent/javassist/runtime/Inner.java | 0 .../agent/javassist/runtime/package.html | 0 .../javassist/scopedpool/ScopedClassPool.java | 0 .../scopedpool/ScopedClassPoolFactory.java | 0 .../ScopedClassPoolFactoryImpl.java | 0 .../scopedpool/ScopedClassPoolRepository.java | 0 .../ScopedClassPoolRepositoryImpl.java | 0 .../scopedpool/SoftValueHashMap.java | 0 .../agent/javassist/scopedpool/package.html | 0 .../agent/javassist/tools/Callback.java | 0 .../wenshuo/agent/javassist/tools/Dump.java | 0 .../agent/javassist/tools/framedump.java | 0 .../agent/javassist/tools/package.html | 0 .../tools/reflect/CannotCreateException.java | 0 .../tools/reflect/CannotInvokeException.java | 0 .../tools/reflect/CannotReflectException.java | 0 .../tools/reflect/ClassMetaobject.java | 0 .../javassist/tools/reflect/Compiler.java | 0 .../agent/javassist/tools/reflect/Loader.java | 0 .../javassist/tools/reflect/Metalevel.java | 0 .../javassist/tools/reflect/Metaobject.java | 0 .../javassist/tools/reflect/Reflection.java | 0 .../agent/javassist/tools/reflect/Sample.java | 0 .../javassist/tools/reflect/package.html | 0 .../javassist/tools/rmi/AppletServer.java | 0 .../javassist/tools/rmi/ObjectImporter.java | 0 .../tools/rmi/ObjectNotFoundException.java | 0 .../agent/javassist/tools/rmi/Proxy.java | 0 .../javassist/tools/rmi/RemoteException.java | 0 .../agent/javassist/tools/rmi/RemoteRef.java | 0 .../agent/javassist/tools/rmi/Sample.java | 0 .../javassist/tools/rmi/StubGenerator.java | 0 .../agent/javassist/tools/rmi/package.html | 0 .../javassist/tools/web/BadHttpRequest.java | 0 .../agent/javassist/tools/web/Viewer.java | 0 .../agent/javassist/tools/web/Webserver.java | 0 .../agent/javassist/tools/web/package.html | 0 .../agent/javassist/util/HotSwapper.java | 0 .../wenshuo/agent/javassist/util/package.html | 0 .../javassist/util/proxy/FactoryHelper.java | 0 .../javassist/util/proxy/MethodFilter.java | 0 .../javassist/util/proxy/MethodHandler.java | 0 .../agent/javassist/util/proxy/Proxy.java | 0 .../javassist/util/proxy/ProxyFactory.java | 0 .../javassist/util/proxy/ProxyObject.java | 0 .../util/proxy/ProxyObjectInputStream.java | 0 .../util/proxy/ProxyObjectOutputStream.java | 0 .../javassist/util/proxy/RuntimeSupport.java | 0 .../javassist/util/proxy/SecurityActions.java | 0 .../javassist/util/proxy/SerializedProxy.java | 0 .../agent/javassist/util/proxy/package.html | 0 .../wenshuo/agent/log/ExecuteLogUtils.java | 0 .../agent/log/MethodExecuteJSONformatter.java | 0 .../wenshuo/agent/log/OutputLogRunnable.java | 0 .../AgentLogClassFileTransformer.java | 0 .../src}/main/resources/agent.properties | 0 .../com/wenshuo/agent/test/TestCtMethod.java | 0 376 files changed, 4030 insertions(+), 2 deletions(-) create mode 100644 agent-analyer/agent-analyzer.iml create mode 100644 agent-analyer/pom.xml create mode 100644 agent-analyer/src/main/java/com/wenshuo/agent/analyzer/AgentAnalyzer.java create mode 100644 agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/AgentLogDO.java create mode 100644 agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/ClassBean.java create mode 100644 agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/MethodBean.java create mode 100644 agent-analyer/src/main/java/com/wenshuo/agent/analyzer/configuration/MvcConfiguration.java create mode 100644 agent-analyer/src/main/java/com/wenshuo/agent/analyzer/controller/CheckLogFileController.java create mode 100644 agent-analyer/src/main/java/com/wenshuo/agent/analyzer/controller/IndexController.java create mode 100644 agent-analyer/src/main/java/com/wenshuo/agent/analyzer/dao/LogFileDao.java create mode 100644 agent-analyer/src/main/java/com/wenshuo/agent/analyzer/service/LogFileService.java create mode 100644 agent-analyer/src/main/resources/application.yml create mode 100644 agent-analyer/src/main/resources/static/html/result.html create mode 100644 agent-analyer/src/main/resources/static/index.html create mode 100644 agent-analyer/src/main/resources/static/js/ZeroClipboard.js create mode 100644 agent-analyer/src/main/resources/static/js/ZeroClipboard.min.js create mode 100644 agent-analyer/src/main/resources/static/js/ZeroClipboard.swf create mode 100644 agent-analyer/src/main/resources/static/js/amsverify.js create mode 100644 agent-analyer/src/main/resources/static/js/auto-line-number.js create mode 100644 agent-analyer/src/main/resources/static/js/diyfunction.js create mode 100644 agent-analyer/src/main/resources/static/js/echarts.common.min.js create mode 100644 agent-analyer/src/main/resources/static/js/jquery-1.7.2.min.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/layui.css create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/layui.mobile.css create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/modules/code.css create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/modules/laydate/default/laydate.css create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/icon-ext.png create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/icon.png create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/layer.css create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/loading-0.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/loading-1.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/loading-2.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/font/iconfont.eot create mode 100644 agent-analyer/src/main/resources/static/js/layui/font/iconfont.svg create mode 100644 agent-analyer/src/main/resources/static/js/layui/font/iconfont.ttf create mode 100644 agent-analyer/src/main/resources/static/js/layui/font/iconfont.woff create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/0.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/1.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/10.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/11.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/12.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/13.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/14.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/15.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/16.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/17.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/18.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/19.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/2.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/20.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/21.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/22.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/23.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/24.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/25.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/26.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/27.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/28.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/29.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/3.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/30.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/31.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/32.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/33.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/34.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/35.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/36.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/37.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/38.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/39.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/4.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/40.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/41.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/42.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/43.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/44.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/45.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/46.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/47.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/48.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/49.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/5.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/50.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/51.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/52.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/53.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/54.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/55.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/56.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/57.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/58.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/59.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/6.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/60.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/61.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/62.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/63.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/64.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/65.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/66.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/67.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/68.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/69.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/7.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/70.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/71.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/8.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/images/face/9.gif create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/carousel.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/code.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/element.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/flow.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/form.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/jquery.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/laydate.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/layedit.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/layer.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/laypage.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/laytpl.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/mobile.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/rate.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/table.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/tree.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/upload.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/lay/modules/util.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/layui.all.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/layui.js create mode 100644 agent-analyer/src/main/resources/static/js/layui/upload.js rename pom.xml => agent/pom.xml (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/Agent.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/AgentUtils.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/ConfigConsts.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/ConfigUtils.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/NamedThreadFactory.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/PojoDetector.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/applog/AppLogFactory.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/applog/CommonsLoggingAppLog.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/applog/ConsoleAppLog.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/applog/IAppLog.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/applog/Slf4jAppLog.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/ByteArrayClassPath.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CannotCompileException.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/ClassClassPath.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/ClassMap.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/ClassPath.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/ClassPool.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/ClassPoolTail.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CodeConverter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtArray.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtBehavior.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtClass.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtClassType.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtConstructor.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtField.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtMember.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtMethod.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtNewClass.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtNewConstructor.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtNewMethod.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtNewNestedClass.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtNewWrappedMethod.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/CtPrimitiveType.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/Loader.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/LoaderClassPath.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/Modifier.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/NotFoundException.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/SerialVersionUID.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/Translator.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/URLClassPath.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/AccessFlag.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/BadBytecode.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ByteArray.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ByteStream.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/Bytecode.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ClassFile.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/CodeIterator.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ConstPool.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/Descriptor.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/FieldInfo.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/LongVector.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/MethodInfo.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/Mnemonic.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/Opcode.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/StackMap.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/StackMapTable.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Type.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Util.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/analysis/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/annotation/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/AccessorMaker.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/CodeGen.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/CompileError.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/Javac.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/KeywordTable.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/Lex.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/MemberResolver.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/NoFieldException.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/Parser.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ProceedHandler.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/SymbolTable.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/SyntaxError.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/TokenId.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/TypeChecker.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTList.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTree.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/Declarator.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/Expr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/IntConst.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/Keyword.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/Member.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/Pair.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/StringL.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/Symbol.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/Variable.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/compiler/ast/Visitor.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/TransformAfter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/TransformBefore.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/TransformCall.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/TransformNew.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/TransformNewClass.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/TransformReadField.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/TransformWriteField.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/convert/Transformer.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/Cast.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/ConstructorCall.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/Expr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/ExprEditor.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/FieldAccess.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/Handler.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/Instanceof.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/MethodCall.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/NewArray.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/NewExpr.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/expr/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/runtime/Cflow.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/runtime/Desc.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/runtime/DotClass.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/runtime/Inner.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/runtime/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/scopedpool/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/Callback.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/Dump.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/framedump.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/Compiler.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/Loader.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/Reflection.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/Sample.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/reflect/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/rmi/Proxy.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/rmi/Sample.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/rmi/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/web/Viewer.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/web/Webserver.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/tools/web/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/HotSwapper.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/Proxy.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/javassist/util/proxy/package.html (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/log/OutputLogRunnable.java (100%) rename {src => agent/src}/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java (100%) rename {src => agent/src}/main/resources/agent.properties (100%) rename {src => agent/src}/test/java/com/wenshuo/agent/test/TestCtMethod.java (100%) diff --git a/.gitignore b/.gitignore index 5a452fe..07f590f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,9 +15,9 @@ target/maven-archiver/pom.properties *.prefs *.project .classpath -/target +target /.settings -/.idea +.idea *.log dependency-reduced-pom.xml diff --git a/agent-analyer/agent-analyzer.iml b/agent-analyer/agent-analyzer.iml new file mode 100644 index 0000000..591b2be --- /dev/null +++ b/agent-analyer/agent-analyzer.iml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/agent-analyer/pom.xml b/agent-analyer/pom.xml new file mode 100644 index 0000000..db7e3bc --- /dev/null +++ b/agent-analyer/pom.xml @@ -0,0 +1,138 @@ + + 4.0.0 + + com.wenshuo + agent-analyzer + 1.0.0 + war + + agent日志分析器 + + + UTF-8 + 1.8 + 2.6.7 + 1.2.83 + 2.15.1 + + + + + + + org.springframework.boot + spring-boot-dependencies + ${springboot.version} + pom + import + + + + + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.commons + commons-lang3 + + + + + + + + + + com.alibaba + fastjson + ${fastjson.version} + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + runtime + + + + commons-io + commons-io + ${commons-io.version} + + + commons-logging + commons-logging + 1.2 + compile + + + + + agent-analyzer + + + src/main/java + + **/*.xml + + + + ${basedir}/src/main/webapp + META-INF/resources + + **/** + + + + src/main/resources + false + + .gitkeep + + + ** + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.springframework.boot + spring-boot-maven-plugin + ${springboot.version} + + + + repackage + + + + + com.wenshuo.agent.analyzer.AgentAnalyzer + true + + + + + diff --git a/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/AgentAnalyzer.java b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/AgentAnalyzer.java new file mode 100644 index 0000000..e5676d4 --- /dev/null +++ b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/AgentAnalyzer.java @@ -0,0 +1,18 @@ +package com.wenshuo.agent.analyzer; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * agent日志分析 + * + * @date 2024/09/06 13:52 + **/ +@SpringBootApplication + +public class AgentAnalyzer { + public static void main(String[] args) { + SpringApplication.run(AgentAnalyzer.class, args); + } + +} diff --git a/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/AgentLogDO.java b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/AgentLogDO.java new file mode 100644 index 0000000..69a24d3 --- /dev/null +++ b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/AgentLogDO.java @@ -0,0 +1,65 @@ +package com.wenshuo.agent.analyzer.bean; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.UUID; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * @date 2024/09/06 14:05 + **/ +@Entity +@Table(name = "T_AGENT_LOG") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AgentLogDO { + + @Id + @GeneratedValue + private UUID id; + + private Date startTime; + + private Date endTime; + + @JSONField(name = "class") + private String className; + + private String methodName; + + private Long counter; + + private Long time; + + private Long avg; + + private Long min; + + private Long max; + + private Long median; + + private Long th90Pct; + + private Long th95Pct; + + private Long th99Pct; + + private String version; + + private String day; + + private String startAndEnd; + + private String fileName; + + private String timeCut; +} diff --git a/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/ClassBean.java b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/ClassBean.java new file mode 100644 index 0000000..aed66bc --- /dev/null +++ b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/ClassBean.java @@ -0,0 +1,39 @@ +package com.wenshuo.agent.analyzer.bean; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * @date 2024/09/06 14:09 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ClassBean { + + private String bh; + + private Date startTime; + + private String start; + + private Date endTime; + + private String end; + + @JSONField(name = "class") + private String className; + + @JSONField(name = "methods") + private String methods; + + private String version; + + private String day; + + private String startAndEnd; +} diff --git a/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/MethodBean.java b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/MethodBean.java new file mode 100644 index 0000000..828a335 --- /dev/null +++ b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/bean/MethodBean.java @@ -0,0 +1,45 @@ +package com.wenshuo.agent.analyzer.bean; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @date 2024/09/06 14:10 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MethodBean { + + @JSONField(name = "name") + private String methodName; + + @JSONField(name = "time") + private Long time; + + @JSONField(name = "avg") + private Long avg; + + @JSONField(name = "counter") + private Long counter; + + @JSONField(name = "min") + private Long min; + + @JSONField(name = "max") + private Long max; + + @JSONField(name = "th50") + private Long median; + + @JSONField(name = "th90") + private Long th90Pct; + + @JSONField(name = "th95") + private Long th95Pct; + + @JSONField(name = "th99") + private Long th99Pct; +} diff --git a/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/configuration/MvcConfiguration.java b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/configuration/MvcConfiguration.java new file mode 100644 index 0000000..60f705a --- /dev/null +++ b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/configuration/MvcConfiguration.java @@ -0,0 +1,104 @@ +package com.wenshuo.agent.analyzer.configuration; + +import com.alibaba.fastjson.serializer.SerializeConfig; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; +import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.springframework.web.servlet.view.JstlView; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @date 2024/09/06 13:59 + **/ +public class MvcConfiguration extends WebMvcConfigurationSupport { + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/*", "/", "").addResourceLocations("classpath:/static/index.html"); + registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); + registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); + registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + super.addResourceHandlers(registry); + } + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/").setViewName("index"); + } + + @Bean + public InternalResourceViewResolver viewResolver() { + InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); + viewResolver.setPrefix("/static/"); + viewResolver.setSuffix(".html"); + + return viewResolver; + } + + /** + * 配置fastjson方式一:(map集合)保留非实体构造方法的null,构造方法中的null格式化为——"" + * 配置fastjson方式二:在SpringBoot启动类中(@SpringBootApplication),注入Bean: HttpMessageConverters + * (list集合+单个对象实例) 中的null,均能够格式为——"" + * + * @param converters + */ + @Override + public void configureMessageConverters(List> converters) { + //创建会话消息实例容器 + FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); + //创建fastJson配置实例 + FastJsonConfig config = new FastJsonConfig(); + config.setSerializerFeatures( + //全局修改日期格式,如果时间是data、时间戳类型,按照这种格式初始化时间 "yyyy-MM-dd HH:mm:ss" + SerializerFeature.WriteDateUseDateFormat, + // 保留 Map 空的字段 + SerializerFeature.WriteMapNullValue, + // 将 String 类型的 null 转成"" + SerializerFeature.WriteNullStringAsEmpty, + // 将 List 类型的 null 转成 [] + SerializerFeature.WriteNullListAsEmpty, + // 将 Boolean 类型的 null 转成 false + SerializerFeature.WriteNullBooleanAsFalse, + // 避免循环引用 + SerializerFeature.DisableCircularReferenceDetect, + //返回Json数据排版格式 + SerializerFeature.PrettyFormat, + // 去除循环引用 + SerializerFeature.DisableCircularReferenceDetect, + // 序列化枚举时使用toString方法 + SerializerFeature.WriteEnumUsingToString + ); + + SerializeConfig serializeConfig = SerializeConfig.globalInstance; + config.setSerializeConfig(serializeConfig); + + + //按字段名称排序后输出-SerializerFeature.SortField + //设置配置实例 + converter.setFastJsonConfig(config); + //设置默认编码方式 + converter.setDefaultCharset(StandardCharsets.UTF_8); + //集合填入媒介类型 + List mediaTypeList = new ArrayList<>(); + // 解决中文乱码问题,相当于在 Controller 上的 @RequestMapping 中加了个属性 produces = "application/json" + mediaTypeList.add(MediaType.APPLICATION_JSON); + //设置支持媒介——装载了解决中文乱码参数 + converter.setSupportedMediaTypes(mediaTypeList); + //添加到会话中 + converters.add(converter); + } +} diff --git a/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/controller/CheckLogFileController.java b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/controller/CheckLogFileController.java new file mode 100644 index 0000000..4c2d696 --- /dev/null +++ b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/controller/CheckLogFileController.java @@ -0,0 +1,187 @@ +package com.wenshuo.agent.analyzer.controller; + + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import java.io.IOException; +import java.text.ParseException; +import java.util.*; + +import com.wenshuo.agent.analyzer.bean.AgentLogDO; +import com.wenshuo.agent.analyzer.dao.LogFileDao; +import com.wenshuo.agent.analyzer.service.LogFileService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +@Controller +@RequestMapping({"/logFileAnalysis"}) +public class CheckLogFileController { + @Autowired + private LogFileDao logFileDao; + + @Autowired + private LogFileService logFileService; + + @RequestMapping({"/upload"}) + @ResponseBody + public JSONObject uploadAndAnalysis(MultipartFile[] file) throws ParseException, IOException { + JSONObject json = new JSONObject(); + boolean success = this.logFileService.saveFileInfoToDatabase(file[0], json); + if (success) { + json.put("code", Integer.valueOf(0)); + } else { + json.put("code", Integer.valueOf(1)); + } + return json; + } + + @RequestMapping({"deleteFiles"}) + @ResponseBody + public Object deleteFiles(String files) { + JSONObject json = new JSONObject(); + try { + this.logFileDao.deleteFiles(files); + json.put("code", Integer.valueOf(0)); + } catch (Exception e) { + json.put("code", Integer.valueOf(1)); + } + return json; + } + + @RequestMapping({"getDays"}) + @ResponseBody + public String getDays() { + List list = this.logFileDao.findFileList(); + return JSONObject.toJSONString(list); + } + + @RequestMapping({"toResultPage"}) + public String toResultPage(Model model, Integer page, String type, String order, String methodName, String className) { + model.addAttribute("methodName", methodName); + model.addAttribute("className", className); + return "html/result"; + } + + @RequestMapping({"toClassResultPage"}) + public String toClassResultPage(Model model, Integer page, String type, String order, String className) { + List findOne = this.logFileService.getDataInfo(null, className); + model.addAttribute("data", JSONObject.toJSONString(findOne)); + model.addAttribute("className", className); + return "html/result"; + } + + @RequestMapping({"getTimes"}) + @ResponseBody + public String getTimes(String files) { + List list = this.logFileDao.findTimeList(files); + return JSONObject.toJSONString(list); + } + + @RequestMapping({"getClassName"}) + @ResponseBody + public String getClassName(String files, String startAndEndTime) { + List list = this.logFileDao.findClassNames(files, startAndEndTime); + return JSONObject.toJSONString(list); + } + + @RequestMapping({"getDetail"}) + @ResponseBody + public String getDetail(String methodName, String className, Long value, Integer type, String days, String startAndEndTime) throws ParseException { + AgentLogDO agentLogDo = new AgentLogDO(); + agentLogDo.setMethodName(methodName); + agentLogDo.setClassName(className); + agentLogDo.setDay(days); + agentLogDo.setStartAndEnd(startAndEndTime); + if (type.intValue() == 1) { + agentLogDo.setTime(value); + } else if (type.intValue() == 2) { + agentLogDo.setCounter(value); + } else if (type.intValue() == 3) { + agentLogDo.setAvg(value); + } + Example logFileExample = Example.of(agentLogDo); + Optional findOne = this.logFileDao.findOne(logFileExample); + return JSONObject.toJSONString(findOne.get()); + } + + @RequestMapping({"getEchartResult"}) + @ResponseBody + public String getEchartResult(String files) { + Map result = new HashMap<>(); + List timePointList = new ArrayList<>(); + List y1Data = new ArrayList<>(); + List y2Data = new ArrayList<>(); + List y3Data = new ArrayList<>(); + List results = this.logFileDao.stasticByFile(files); + for (Map statics : results) { + String timePoint = statics.get("timeCut").toString().substring(10); + Long count = (Long)statics.get("totalCounter"); + y1Data.add(count.toString()); + Long time = (Long)statics.get("totalTime"); + y2Data.add(time.toString()); + if (count.longValue() != 0L) { + Long avg = Long.valueOf(time.longValue() / count.longValue()); + y3Data.add(avg.toString()); + } else { + y3Data.add("-"); + } + timePointList.add(timePoint); + } + result.put("xData", JSONObject.toJSONString(timePointList)); + result.put("y1Data", JSONObject.toJSONString(y1Data)); + result.put("y2Data", JSONObject.toJSONString(y2Data)); + result.put("y3Data", JSONObject.toJSONString(y3Data)); + return JSONObject.toJSONString(result); + } + + @RequestMapping({"getDetailTableResult"}) + @ResponseBody + public String getDetailTableResult(Integer page, String type, String order, String className, String methodName) throws ParseException { + Map result = new HashMap<>(); + AgentLogDO agentLogDo = new AgentLogDO(); + if (StringUtils.isNotEmpty(methodName)) + agentLogDo.setMethodName(methodName); + if (StringUtils.isNotEmpty(className)) + agentLogDo.setClassName(className); + Example logFileExample = Example.of(agentLogDo); + String sortString = "time"; + Pageable pageable = this.logFileService.getPageInfo(page, type, order, result, sortString); + Page pageInfo = this.logFileDao.findAll(logFileExample, pageable); + result.put("code", Integer.valueOf(1)); + result.put("count", Long.valueOf(pageInfo.getTotalElements())); + result.put("data", JSON.toJSON(pageInfo.getContent())); + return JSONObject.toJSONString(result); + } + + @RequestMapping({"getTableResult"}) + @ResponseBody + public String getTableResult(String files, String startAndEndTime, Integer page, String type, String order, String className, String methodName) throws ParseException { + Map result = new HashMap<>(); + AgentLogDO agentLogDo = new AgentLogDO(); + agentLogDo.setFileName(files); + if (!startAndEndTime.equals("null")) + agentLogDo.setStartAndEnd(startAndEndTime); + if (StringUtils.isNotEmpty(methodName)) + agentLogDo.setMethodName(methodName); + if (StringUtils.isNotEmpty(className)) + agentLogDo.setClassName(className); + ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("className", ExampleMatcher.GenericPropertyMatchers.contains()); + Example logFileExample = Example.of(agentLogDo, matcher); + String sortString = "avg"; + Pageable pageable = this.logFileService.getPageInfo(page, type, order, result, sortString); + Page pageInfo = this.logFileDao.findAll(logFileExample, pageable); + result.put("code", Integer.valueOf(1)); + result.put("count", Long.valueOf(pageInfo.getTotalElements())); + result.put("data", JSON.toJSON(pageInfo.getContent())); + return JSONObject.toJSONString(result); + } +} diff --git a/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/controller/IndexController.java b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/controller/IndexController.java new file mode 100644 index 0000000..b260f58 --- /dev/null +++ b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/controller/IndexController.java @@ -0,0 +1,16 @@ +package com.wenshuo.agent.analyzer.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @date 2024/09/06 15:00 + **/ +@Controller +public class IndexController { + + // @RequestMapping({"/"}) + public String index() { + return "jsp/prc"; + } +} diff --git a/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/dao/LogFileDao.java b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/dao/LogFileDao.java new file mode 100644 index 0000000..467fd99 --- /dev/null +++ b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/dao/LogFileDao.java @@ -0,0 +1,41 @@ +package com.wenshuo.agent.analyzer.dao; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import com.wenshuo.agent.analyzer.bean.AgentLogDO; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +public interface LogFileDao extends JpaRepository { + @Query("select t from AgentLogDO t where t.className = ?1 and t.methodName = ?2 and t.startTime = ?3 and t.endTime =?4") + List findByClassName(String paramString1, String paramString2, Date paramDate1, Date paramDate2); + + @Query("select startAndEnd from AgentLogDO t where t.fileName = ?1 group by startAndEnd order by startAndEnd") + List findTimeList(String paramString); + + @Query("select timeCut from AgentLogDO t where t.fileName = ?1 group by timeCut order by timeCut") + List findTimeCutList(String paramString); + + @Query("SELECT fileName FROM AgentLogDO group by fileName") + List findFileList(); + + @Modifying + @Query("delete from AgentLogDO t where t.fileName = ?1") + void deleteFiles(String paramString); + + @Query("select className from AgentLogDO t where t.fileName = ?1 and t.startAndEnd = ?2 ") + List findClassNames(String paramString1, String paramString2); + + @Query("select sum(counter) from AgentLogDO t where t.fileName = ?1 and t.timeCut = ?2 ") + Long selectCountInfo(String paramString1, String paramString2); + + @Query("select sum(time) from AgentLogDO t where t.fileName = ?1 and t.timeCut = ?2 ") + Long selectTimeInfo(String paramString1, String paramString2); + + @Query("select t.timeCut as timeCut,sum(time) as totalTime,sum(counter) as totalCounter from AgentLogDO t where t.fileName = ?1 group by timeCut order by timeCut ") + List stasticByFile(String paramString); +} diff --git a/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/service/LogFileService.java b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/service/LogFileService.java new file mode 100644 index 0000000..03a0c2d --- /dev/null +++ b/agent-analyer/src/main/java/com/wenshuo/agent/analyzer/service/LogFileService.java @@ -0,0 +1,150 @@ +package com.wenshuo.agent.analyzer.service; + +import com.alibaba.fastjson.JSONObject; +import com.wenshuo.agent.analyzer.bean.AgentLogDO; +import com.wenshuo.agent.analyzer.bean.ClassBean; +import com.wenshuo.agent.analyzer.bean.MethodBean; +import com.wenshuo.agent.analyzer.dao.LogFileDao; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @date 2024/09/06 14:25 + **/ +@Service +public class LogFileService { + private static Log log = LogFactory.getLog(LogFileService.class); + + private static final String PATTERNS = "yyyy-MM-dd hh:mm:ss"; + + @Autowired + private LogFileDao logFileDao; + + public boolean saveFileInfoToDatabase(MultipartFile file, JSONObject json) throws IOException, ParseException { + long start = System.currentTimeMillis(); + boolean success = validateAgentLogFile(file, json); + if (!success) + return false; + String fileName = file.getOriginalFilename(); + String agentLogStr = readAgentLog(file); + List agentLogArray = JSONObject.parseArray(agentLogStr, ClassBean.class); + long parseArrayEnd = System.currentTimeMillis(); + log.info("转换【" + fileName + "】成ClassBean成功,耗时【" + (parseArrayEnd - start) + "】毫秒,共有【" + agentLogArray.size() + "】条"); + List agentLogDOList = new ArrayList<>(agentLogArray.size() * 10); + for (ClassBean classBean : agentLogArray) { + List methodList = JSONObject.parseArray(classBean.getMethods(), MethodBean.class); + if (!CollectionUtils.isEmpty(methodList)) + for (MethodBean methodBean : methodList) { + AgentLogDO agentLogDo = buildAgentLogDO(fileName, classBean, methodBean); + agentLogDOList.add(agentLogDo); + } + } + long convertToBeanEnd = System.currentTimeMillis(); + log.info("转换【" + fileName + "】成功,耗时【" + (convertToBeanEnd - start) + "】毫秒"); + if (!CollectionUtils.isEmpty(agentLogDOList)) { + this.logFileDao.saveAll(agentLogDOList); + this.logFileDao.flush(); + } + long end = System.currentTimeMillis(); + log.info("文件【" + fileName + "】对应的数据保存到数据库成功,耗时【" + (end - convertToBeanEnd) + "】毫秒"); + return true; + } + + private AgentLogDO buildAgentLogDO(String fileName, ClassBean classBean, MethodBean methodBean) throws ParseException { + AgentLogDO agentLogDo = new AgentLogDO(); + agentLogDo.setMethodName(methodBean.getMethodName()); + agentLogDo.setCounter(methodBean.getCounter()); + agentLogDo.setTime(methodBean.getTime()); + if (methodBean.getAvg() != null) { + agentLogDo.setAvg(methodBean.getAvg()); + } else { + agentLogDo.setAvg(Long.valueOf(methodBean.getTime().longValue() / methodBean.getCounter().longValue())); + } + agentLogDo.setMin(methodBean.getMin()); + agentLogDo.setMedian(methodBean.getMedian()); + agentLogDo.setMax(methodBean.getMax()); + agentLogDo.setTh90Pct(methodBean.getTh90Pct()); + agentLogDo.setTh95Pct(methodBean.getTh95Pct()); + agentLogDo.setTh99Pct(methodBean.getTh99Pct()); + agentLogDo.setClassName(classBean.getClassName()); + agentLogDo.setDay(classBean.getStart().substring(0, 10)); + agentLogDo.setStartAndEnd(classBean.getStart() + " 至 " + classBean.getEnd()); + agentLogDo.setStartTime(DateUtils.parseDate(classBean.getStart(), new String[] { "yyyy-MM-dd hh:mm:ss" })); + agentLogDo.setTimeCut(classBean.getStart().substring(0, 13)); + agentLogDo.setEndTime(DateUtils.parseDate(classBean.getEnd(), new String[] { "yyyy-MM-dd hh:mm:ss" })); + agentLogDo.setFileName(fileName); + return agentLogDo; + } + + private String readAgentLog(MultipartFile file) throws IOException { + try (InputStream input = file.getInputStream()) { + return IOUtils.toString(input, "UTF-8"); + } + } + + private boolean validateAgentLogFile(MultipartFile file, JSONObject json) { + if (file.getSize() == 0L) { + json.put("code", Integer.valueOf(1)); + json.put("message", "上传的文档不存在"); + return false; + } + String fileName = file.getOriginalFilename(); + AgentLogDO agentLogDoForQuery = new AgentLogDO(); + agentLogDoForQuery.setFileName(fileName); + Example example = Example.of(agentLogDoForQuery); + if (this.logFileDao.exists(example)) { + String message = "此文档已存在,文档名为" + fileName; + log.info(message); + json.put("message", message); + return false; + } + return true; + } + + public List getDataInfo(String methodName, String className) { + AgentLogDO agentLogDo = new AgentLogDO(); + agentLogDo.setClassName(className); + if (StringUtils.isNotEmpty(methodName)) + agentLogDo.setMethodName(methodName); + Example logFileExample = Example.of(agentLogDo); + String sortString = "time"; + Sort sort = Sort.by(Sort.Direction.DESC, new String[] { sortString }); + return this.logFileDao.findAll(logFileExample, sort); + } + + public Pageable getPageInfo(Integer page, String type, String order, Map result, String sortString) { + PageRequest pageRequest; + if (type != null) + sortString = type; + Sort sort = Sort.by(Sort.Direction.DESC, new String[] { sortString }); + if (order != null && order.contains("asc")) + sort = Sort.by(Sort.Direction.ASC, new String[] { sortString }); + Pageable pageable = null; + if (page != null && page.intValue() > 1) { + pageRequest = PageRequest.of(page.intValue() - 1, 100, sort); + result.put("pages", page); + } else { + pageRequest = PageRequest.of(0, 100, sort); + result.put("pages", Integer.valueOf(1)); + } + return (Pageable)pageRequest; + } +} diff --git a/agent-analyer/src/main/resources/application.yml b/agent-analyer/src/main/resources/application.yml new file mode 100644 index 0000000..42df27d --- /dev/null +++ b/agent-analyer/src/main/resources/application.yml @@ -0,0 +1,40 @@ +server: + port: 8080 +spring: + datasource: + url: jdbc:h2:mem:~/h2test;DB_CLOSE_DELAY=-1 + platform: h2 + username: sa + password: + driverClassName: org.h2.Driver + jpa: + database-platform: org.hibernate.dialect.H2Dialect + hibernate: + ddl-auto: update + properties: + hibernate: + show_sql: false + use_sql_comments: false + format_sql: false + generate_statistics : false + cache : + use_second_level_cache : false + jdbc: + batch_size : 50 + order_inserts : true + order_updates : true + h2: + console: + enabled: true + path: /console + settings: + trace: false + web-allow-others: true + servlet: + multipart: + max-file-size: 1000MB + max-request-size: 1000MB +logging: + level: + root: info + com.wenshuo: debug diff --git a/agent-analyer/src/main/resources/static/html/result.html b/agent-analyer/src/main/resources/static/html/result.html new file mode 100644 index 0000000..de816a2 --- /dev/null +++ b/agent-analyer/src/main/resources/static/html/result.html @@ -0,0 +1,100 @@ + + + + + +Insert title here + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/index.html b/agent-analyer/src/main/resources/static/index.html new file mode 100644 index 0000000..1fa50c3 --- /dev/null +++ b/agent-analyer/src/main/resources/static/index.html @@ -0,0 +1,634 @@ + + + + + agent日志自动化工具 + + + + +
+
+ + + +
+ + +
+ +
+
+ + +
+
+
+ +
+ +
+ +
+ + + + + + + + + + +
文件名大小状态操作
+
+ + +
+ + +
+ + + +
+
+
+ +
+ + +
+ + +
+ + +
+ +
+ +
+ +
+ + +
+
+
+
+ +
+
+ + +
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/ZeroClipboard.js b/agent-analyer/src/main/resources/static/js/ZeroClipboard.js new file mode 100644 index 0000000..8799966 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/ZeroClipboard.js @@ -0,0 +1,458 @@ +/*! +* ZeroClipboard +* The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface. +* Copyright (c) 2013 Jon Rohan, James M. Greene +* Licensed MIT +* http://zeroclipboard.org/ +* v1.2.0-beta.4 +*/ +(function() { + "use strict"; + var _camelizeCssPropName = function() { + var matcherRegex = /\-([a-z])/g, replacerFn = function(match, group) { + return group.toUpperCase(); + }; + return function(prop) { + return prop.replace(matcherRegex, replacerFn); + }; + }(); + var _getStyle = function(el, prop) { + var value, camelProp, tagName, possiblePointers, i, len; + if (window.getComputedStyle) { + value = window.getComputedStyle(el, null).getPropertyValue(prop); + } else { + camelProp = _camelizeCssPropName(prop); + if (el.currentStyle) { + value = el.currentStyle[camelProp]; + } else { + value = el.style[camelProp]; + } + } + if (prop === "cursor") { + if (!value || value === "auto") { + tagName = el.tagName.toLowerCase(); + possiblePointers = [ "a" ]; + for (i = 0, len = possiblePointers.length; i < len; i++) { + if (tagName === possiblePointers[i]) { + return "pointer"; + } + } + } + } + return value; + }; + var _elementMouseOver = function(event) { + if (!ZeroClipboard.prototype._singleton) return; + if (!event) { + event = window.event; + } + var target; + if (this !== window) { + target = this; + } else if (event.target) { + target = event.target; + } else if (event.srcElement) { + target = event.srcElement; + } + ZeroClipboard.prototype._singleton.setCurrent(target); + }; + var _addEventHandler = function(element, method, func) { + if (element.addEventListener) { + element.addEventListener(method, func, false); + } else if (element.attachEvent) { + element.attachEvent("on" + method, func); + } + }; + var _removeEventHandler = function(element, method, func) { + if (element.removeEventListener) { + element.removeEventListener(method, func, false); + } else if (element.detachEvent) { + element.detachEvent("on" + method, func); + } + }; + var _addClass = function(element, value) { + if (element.addClass) { + element.addClass(value); + return element; + } + if (value && typeof value === "string") { + var classNames = (value || "").split(/\s+/); + if (element.nodeType === 1) { + if (!element.className) { + element.className = value; + } else { + var className = " " + element.className + " ", setClass = element.className; + for (var c = 0, cl = classNames.length; c < cl; c++) { + if (className.indexOf(" " + classNames[c] + " ") < 0) { + setClass += " " + classNames[c]; + } + } + element.className = setClass.replace(/^\s+|\s+$/g, ""); + } + } + } + return element; + }; + var _removeClass = function(element, value) { + if (element.removeClass) { + element.removeClass(value); + return element; + } + if (value && typeof value === "string" || value === undefined) { + var classNames = (value || "").split(/\s+/); + if (element.nodeType === 1 && element.className) { + if (value) { + var className = (" " + element.className + " ").replace(/[\n\t]/g, " "); + for (var c = 0, cl = classNames.length; c < cl; c++) { + className = className.replace(" " + classNames[c] + " ", " "); + } + element.className = className.replace(/^\s+|\s+$/g, ""); + } else { + element.className = ""; + } + } + } + return element; + }; + var _getZoomFactor = function() { + var rect, physicalWidth, logicalWidth, zoomFactor = 1; + if (typeof document.body.getBoundingClientRect === "function") { + rect = document.body.getBoundingClientRect(); + physicalWidth = rect.right - rect.left; + logicalWidth = document.body.offsetWidth; + zoomFactor = Math.round(physicalWidth / logicalWidth * 100) / 100; + } + return zoomFactor; + }; + var _getDOMObjectPosition = function(obj) { + var info = { + left: 0, + top: 0, + width: 0, + height: 0, + zIndex: 999999999 + }; + var zi = _getStyle(obj, "z-index"); + if (zi && zi !== "auto") { + info.zIndex = parseInt(zi, 10); + } + if (obj.getBoundingClientRect) { + var rect = obj.getBoundingClientRect(); + var pageXOffset, pageYOffset, zoomFactor; + if ("pageXOffset" in window && "pageYOffset" in window) { + pageXOffset = window.pageXOffset; + pageYOffset = window.pageYOffset; + } else { + zoomFactor = _getZoomFactor(); + pageXOffset = Math.round(document.documentElement.scrollLeft / zoomFactor); + pageYOffset = Math.round(document.documentElement.scrollTop / zoomFactor); + } + var leftBorderWidth = document.documentElement.clientLeft || 0; + var topBorderWidth = document.documentElement.clientTop || 0; + info.left = rect.left + pageXOffset - leftBorderWidth; + info.top = rect.top + pageYOffset - topBorderWidth; + info.width = "width" in rect ? rect.width : rect.right - rect.left; + info.height = "height" in rect ? rect.height : rect.bottom - rect.top; + } + return info; + }; + var _noCache = function(path, options) { + var useNoCache = !(options && options.useNoCache === false); + if (useNoCache) { + return (path.indexOf("?") === -1 ? "?" : "&") + "nocache=" + new Date().getTime(); + } else { + return ""; + } + }; + var _vars = function(options) { + var str = []; + var origins = []; + if (options.trustedOrigins) { + if (typeof options.trustedOrigins === "string") { + origins = origins.push(options.trustedOrigins); + } else if (typeof options.trustedOrigins === "object" && "length" in options.trustedOrigins) { + origins = origins.concat(options.trustedOrigins); + } + } + if (options.trustedDomains) { + if (typeof options.trustedDomains === "string") { + origins = origins.push(options.trustedDomains); + } else if (typeof options.trustedDomains === "object" && "length" in options.trustedDomains) { + origins = origins.concat(options.trustedDomains); + } + } + if (origins.length) { + str.push("trustedOrigins=" + encodeURIComponent(origins.join(","))); + } + if (typeof options.amdModuleId === "string" && options.amdModuleId) { + str.push("amdModuleId=" + encodeURIComponent(options.amdModuleId)); + } + if (typeof options.cjsModuleId === "string" && options.cjsModuleId) { + str.push("cjsModuleId=" + encodeURIComponent(options.cjsModuleId)); + } + return str.join("&"); + }; + var _inArray = function(elem, array) { + if (array.indexOf) { + return array.indexOf(elem); + } + for (var i = 0, length = array.length; i < length; i++) { + if (array[i] === elem) { + return i; + } + } + return -1; + }; + var _prepGlue = function(elements) { + if (typeof elements === "string") throw new TypeError("ZeroClipboard doesn't accept query strings."); + if (!elements.length) return [ elements ]; + return elements; + }; + var _dispatchCallback = function(func, element, instance, args, async) { + if (async) { + window.setTimeout(function() { + func.call(element, instance, args); + }, 0); + } else { + func.call(element, instance, args); + } + }; + var ZeroClipboard = function(elements, options) { + if (elements) (ZeroClipboard.prototype._singleton || this).glue(elements); + if (ZeroClipboard.prototype._singleton) return ZeroClipboard.prototype._singleton; + ZeroClipboard.prototype._singleton = this; + this.options = {}; + for (var kd in _defaults) this.options[kd] = _defaults[kd]; + for (var ko in options) this.options[ko] = options[ko]; + this.handlers = {}; + if (ZeroClipboard.detectFlashSupport()) _bridge(); + }; + var currentElement, gluedElements = []; + ZeroClipboard.prototype.setCurrent = function(element) { + currentElement = element; + this.reposition(); + var titleAttr = element.getAttribute("title"); + if (titleAttr) { + this.setTitle(titleAttr); + } + var useHandCursor = this.options.forceHandCursor === true || _getStyle(element, "cursor") === "pointer"; + _setHandCursor.call(this, useHandCursor); + }; + ZeroClipboard.prototype.setText = function(newText) { + if (newText && newText !== "") { + this.options.text = newText; + if (this.ready()) this.flashBridge.setText(newText); + } + }; + ZeroClipboard.prototype.setTitle = function(newTitle) { + if (newTitle && newTitle !== "") this.htmlBridge.setAttribute("title", newTitle); + }; + ZeroClipboard.prototype.setSize = function(width, height) { + if (this.ready()) this.flashBridge.setSize(width, height); + }; + ZeroClipboard.prototype.setHandCursor = function(enabled) { + enabled = typeof enabled === "boolean" ? enabled : !!enabled; + _setHandCursor.call(this, enabled); + this.options.forceHandCursor = enabled; + }; + var _setHandCursor = function(enabled) { + if (this.ready()) this.flashBridge.setHandCursor(enabled); + }; + ZeroClipboard.version = "1.2.0-beta.4"; + var _defaults = { + moviePath: "ZeroClipboard.swf", + trustedOrigins: null, + text: null, + hoverClass: "zeroclipboard-is-hover", + activeClass: "zeroclipboard-is-active", + allowScriptAccess: "sameDomain", + useNoCache: true, + forceHandCursor: false + }; + ZeroClipboard.setDefaults = function(options) { + for (var ko in options) _defaults[ko] = options[ko]; + }; + ZeroClipboard.destroy = function() { + ZeroClipboard.prototype._singleton.unglue(gluedElements); + var bridge = ZeroClipboard.prototype._singleton.htmlBridge; + bridge.parentNode.removeChild(bridge); + delete ZeroClipboard.prototype._singleton; + }; + ZeroClipboard.detectFlashSupport = function() { + var hasFlash = false; + if (typeof ActiveXObject === "function") { + try { + if (new ActiveXObject("ShockwaveFlash.ShockwaveFlash")) { + hasFlash = true; + } + } catch (error) {} + } + if (!hasFlash && navigator.mimeTypes["application/x-shockwave-flash"]) { + hasFlash = true; + } + return hasFlash; + }; + var _amdModuleId = null; + var _cjsModuleId = null; + var _bridge = function() { + var client = ZeroClipboard.prototype._singleton; + var container = document.getElementById("global-zeroclipboard-html-bridge"); + if (!container) { + var opts = {}; + for (var ko in client.options) opts[ko] = client.options[ko]; + opts.amdModuleId = _amdModuleId; + opts.cjsModuleId = _cjsModuleId; + var flashvars = _vars(opts); + var html = ' '; + container = document.createElement("div"); + container.id = "global-zeroclipboard-html-bridge"; + container.setAttribute("class", "global-zeroclipboard-container"); + container.setAttribute("data-clipboard-ready", false); + container.style.position = "absolute"; + container.style.left = "-9999px"; + container.style.top = "-9999px"; + container.style.width = "15px"; + container.style.height = "15px"; + container.style.zIndex = "9999"; + container.innerHTML = html; + document.body.appendChild(container); + } + client.htmlBridge = container; + client.flashBridge = document["global-zeroclipboard-flash-bridge"] || container.children[0].lastElementChild; + }; + ZeroClipboard.prototype.resetBridge = function() { + this.htmlBridge.style.left = "-9999px"; + this.htmlBridge.style.top = "-9999px"; + this.htmlBridge.removeAttribute("title"); + this.htmlBridge.removeAttribute("data-clipboard-text"); + _removeClass(currentElement, this.options.activeClass); + currentElement = null; + this.options.text = null; + }; + ZeroClipboard.prototype.ready = function() { + var ready = this.htmlBridge.getAttribute("data-clipboard-ready"); + return ready === "true" || ready === true; + }; + ZeroClipboard.prototype.reposition = function() { + if (!currentElement) return false; + var pos = _getDOMObjectPosition(currentElement); + this.htmlBridge.style.top = pos.top + "px"; + this.htmlBridge.style.left = pos.left + "px"; + this.htmlBridge.style.width = pos.width + "px"; + this.htmlBridge.style.height = pos.height + "px"; + this.htmlBridge.style.zIndex = pos.zIndex + 1; + this.setSize(pos.width, pos.height); + }; + ZeroClipboard.dispatch = function(eventName, args) { + ZeroClipboard.prototype._singleton.receiveEvent(eventName, args); + }; + ZeroClipboard.prototype.on = function(eventName, func) { + var events = eventName.toString().split(/\s/g); + for (var i = 0; i < events.length; i++) { + eventName = events[i].toLowerCase().replace(/^on/, ""); + if (!this.handlers[eventName]) this.handlers[eventName] = func; + } + if (this.handlers.noflash && !ZeroClipboard.detectFlashSupport()) { + this.receiveEvent("onNoFlash", null); + } + }; + ZeroClipboard.prototype.addEventListener = ZeroClipboard.prototype.on; + ZeroClipboard.prototype.off = function(eventName, func) { + var events = eventName.toString().split(/\s/g); + for (var i = 0; i < events.length; i++) { + eventName = events[i].toLowerCase().replace(/^on/, ""); + for (var event in this.handlers) { + if (event === eventName && this.handlers[event] === func) { + delete this.handlers[event]; + } + } + } + }; + ZeroClipboard.prototype.removeEventListener = ZeroClipboard.prototype.off; + ZeroClipboard.prototype.receiveEvent = function(eventName, args) { + eventName = eventName.toString().toLowerCase().replace(/^on/, ""); + var element = currentElement; + var performCallbackAsync = true; + switch (eventName) { + case "load": + if (args && parseFloat(args.flashVersion.replace(",", ".").replace(/[^0-9\.]/gi, "")) < 10) { + this.receiveEvent("onWrongFlash", { + flashVersion: args.flashVersion + }); + return; + } + this.htmlBridge.setAttribute("data-clipboard-ready", true); + break; + + case "mouseover": + _addClass(element, this.options.hoverClass); + break; + + case "mouseout": + _removeClass(element, this.options.hoverClass); + this.resetBridge(); + break; + + case "mousedown": + _addClass(element, this.options.activeClass); + break; + + case "mouseup": + _removeClass(element, this.options.activeClass); + break; + + case "datarequested": + var targetId = element.getAttribute("data-clipboard-target"), targetEl = !targetId ? null : document.getElementById(targetId); + if (targetEl) { + var textContent = targetEl.value || targetEl.textContent || targetEl.innerText; + if (textContent) this.setText(textContent); + } else { + var defaultText = element.getAttribute("data-clipboard-text"); + if (defaultText) this.setText(defaultText); + } + performCallbackAsync = false; + break; + + case "complete": + this.options.text = null; + break; + } + if (this.handlers[eventName]) { + var func = this.handlers[eventName]; + if (typeof func === "string" && typeof window[func] === "function") { + func = window[func]; + } + if (typeof func === "function") { + _dispatchCallback(func, element, this, args, performCallbackAsync); + } + } + }; + ZeroClipboard.prototype.glue = function(elements) { + elements = _prepGlue(elements); + for (var i = 0; i < elements.length; i++) { + if (_inArray(elements[i], gluedElements) == -1) { + gluedElements.push(elements[i]); + _addEventHandler(elements[i], "mouseover", _elementMouseOver); + } + } + }; + ZeroClipboard.prototype.unglue = function(elements) { + elements = _prepGlue(elements); + for (var i = 0; i < elements.length; i++) { + _removeEventHandler(elements[i], "mouseover", _elementMouseOver); + var arrayIndex = _inArray(elements[i], gluedElements); + if (arrayIndex != -1) gluedElements.splice(arrayIndex, 1); + } + }; + if (typeof define === "function" && define.amd) { + define([ "require", "exports", "module" ], function(require, exports, module) { + _amdModuleId = module && module.id || null; + return ZeroClipboard; + }); + } else if (typeof module !== "undefined" && module) { + _cjsModuleId = module.id || null; + module.exports = ZeroClipboard; + } else { + window.ZeroClipboard = ZeroClipboard; + } +})(); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/ZeroClipboard.min.js b/agent-analyer/src/main/resources/static/js/ZeroClipboard.min.js new file mode 100644 index 0000000..9e583aa --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/ZeroClipboard.min.js @@ -0,0 +1,9 @@ +/*! +* ZeroClipboard +* The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface. +* Copyright (c) 2013 Jon Rohan, James M. Greene +* Licensed MIT +* http://zeroclipboard.org/ +* v1.2.0-beta.4 +*/ +!function(){"use strict";var a,b=function(){var a=/\-([a-z])/g,b=function(a,b){return b.toUpperCase()};return function(c){return c.replace(a,b)}}(),c=function(a,c){var d,e,f,g,h,i;if(window.getComputedStyle?d=window.getComputedStyle(a,null).getPropertyValue(c):(e=b(c),d=a.currentStyle?a.currentStyle[e]:a.style[e]),"cursor"===c&&(!d||"auto"===d))for(f=a.tagName.toLowerCase(),g=["a"],h=0,i=g.length;i>h;h++)if(f===g[h])return"pointer";return d},d=function(a){if(p.prototype._singleton){a||(a=window.event);var b;this!==window?b=this:a.target?b=a.target:a.srcElement&&(b=a.srcElement),p.prototype._singleton.setCurrent(b)}},e=function(a,b,c){a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,c)},f=function(a,b,c){a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent&&a.detachEvent("on"+b,c)},g=function(a,b){if(a.addClass)return a.addClass(b),a;if(b&&"string"==typeof b){var c=(b||"").split(/\s+/);if(1===a.nodeType)if(a.className){for(var d=" "+a.className+" ",e=a.className,f=0,g=c.length;g>f;f++)d.indexOf(" "+c[f]+" ")<0&&(e+=" "+c[f]);a.className=e.replace(/^\s+|\s+$/g,"")}else a.className=b}return a},h=function(a,b){if(a.removeClass)return a.removeClass(b),a;if(b&&"string"==typeof b||void 0===b){var c=(b||"").split(/\s+/);if(1===a.nodeType&&a.className)if(b){for(var d=(" "+a.className+" ").replace(/[\n\t]/g," "),e=0,f=c.length;f>e;e++)d=d.replace(" "+c[e]+" "," ");a.className=d.replace(/^\s+|\s+$/g,"")}else a.className=""}return a},i=function(){var a,b,c,d=1;return"function"==typeof document.body.getBoundingClientRect&&(a=document.body.getBoundingClientRect(),b=a.right-a.left,c=document.body.offsetWidth,d=Math.round(100*(b/c))/100),d},j=function(a){var b={left:0,top:0,width:0,height:0,zIndex:999999999},d=c(a,"z-index");if(d&&"auto"!==d&&(b.zIndex=parseInt(d,10)),a.getBoundingClientRect){var e,f,g,h=a.getBoundingClientRect();"pageXOffset"in window&&"pageYOffset"in window?(e=window.pageXOffset,f=window.pageYOffset):(g=i(),e=Math.round(document.documentElement.scrollLeft/g),f=Math.round(document.documentElement.scrollTop/g));var j=document.documentElement.clientLeft||0,k=document.documentElement.clientTop||0;b.left=h.left+e-j,b.top=h.top+f-k,b.width="width"in h?h.width:h.right-h.left,b.height="height"in h?h.height:h.bottom-h.top}return b},k=function(a,b){var c=!(b&&b.useNoCache===!1);return c?(-1===a.indexOf("?")?"?":"&")+"nocache="+(new Date).getTime():""},l=function(a){var b=[],c=[];return a.trustedOrigins&&("string"==typeof a.trustedOrigins?c=c.push(a.trustedOrigins):"object"==typeof a.trustedOrigins&&"length"in a.trustedOrigins&&(c=c.concat(a.trustedOrigins))),a.trustedDomains&&("string"==typeof a.trustedDomains?c=c.push(a.trustedDomains):"object"==typeof a.trustedDomains&&"length"in a.trustedDomains&&(c=c.concat(a.trustedDomains))),c.length&&b.push("trustedOrigins="+encodeURIComponent(c.join(","))),"string"==typeof a.amdModuleId&&a.amdModuleId&&b.push("amdModuleId="+encodeURIComponent(a.amdModuleId)),"string"==typeof a.cjsModuleId&&a.cjsModuleId&&b.push("cjsModuleId="+encodeURIComponent(a.cjsModuleId)),b.join("&")},m=function(a,b){if(b.indexOf)return b.indexOf(a);for(var c=0,d=b.length;d>c;c++)if(b[c]===a)return c;return-1},n=function(a){if("string"==typeof a)throw new TypeError("ZeroClipboard doesn't accept query strings.");return a.length?a:[a]},o=function(a,b,c,d,e){e?window.setTimeout(function(){a.call(b,c,d)},0):a.call(b,c,d)},p=function(a,b){if(a&&(p.prototype._singleton||this).glue(a),p.prototype._singleton)return p.prototype._singleton;p.prototype._singleton=this,this.options={};for(var c in s)this.options[c]=s[c];for(var d in b)this.options[d]=b[d];this.handlers={},p.detectFlashSupport()&&v()},q=[];p.prototype.setCurrent=function(b){a=b,this.reposition();var d=b.getAttribute("title");d&&this.setTitle(d);var e=this.options.forceHandCursor===!0||"pointer"===c(b,"cursor");r.call(this,e)},p.prototype.setText=function(a){a&&""!==a&&(this.options.text=a,this.ready()&&this.flashBridge.setText(a))},p.prototype.setTitle=function(a){a&&""!==a&&this.htmlBridge.setAttribute("title",a)},p.prototype.setSize=function(a,b){this.ready()&&this.flashBridge.setSize(a,b)},p.prototype.setHandCursor=function(a){a="boolean"==typeof a?a:!!a,r.call(this,a),this.options.forceHandCursor=a};var r=function(a){this.ready()&&this.flashBridge.setHandCursor(a)};p.version="1.2.0-beta.4";var s={moviePath:"ZeroClipboard.swf",trustedOrigins:null,text:null,hoverClass:"zeroclipboard-is-hover",activeClass:"zeroclipboard-is-active",allowScriptAccess:"sameDomain",useNoCache:!0,forceHandCursor:!1};p.setDefaults=function(a){for(var b in a)s[b]=a[b]},p.destroy=function(){p.prototype._singleton.unglue(q);var a=p.prototype._singleton.htmlBridge;a.parentNode.removeChild(a),delete p.prototype._singleton},p.detectFlashSupport=function(){var a=!1;if("function"==typeof ActiveXObject)try{new ActiveXObject("ShockwaveFlash.ShockwaveFlash")&&(a=!0)}catch(b){}return!a&&navigator.mimeTypes["application/x-shockwave-flash"]&&(a=!0),a};var t=null,u=null,v=function(){var a=p.prototype._singleton,b=document.getElementById("global-zeroclipboard-html-bridge");if(!b){var c={};for(var d in a.options)c[d]=a.options[d];c.amdModuleId=t,c.cjsModuleId=u;var e=l(c),f=' ';b=document.createElement("div"),b.id="global-zeroclipboard-html-bridge",b.setAttribute("class","global-zeroclipboard-container"),b.setAttribute("data-clipboard-ready",!1),b.style.position="absolute",b.style.left="-9999px",b.style.top="-9999px",b.style.width="15px",b.style.height="15px",b.style.zIndex="9999",b.innerHTML=f,document.body.appendChild(b)}a.htmlBridge=b,a.flashBridge=document["global-zeroclipboard-flash-bridge"]||b.children[0].lastElementChild};p.prototype.resetBridge=function(){this.htmlBridge.style.left="-9999px",this.htmlBridge.style.top="-9999px",this.htmlBridge.removeAttribute("title"),this.htmlBridge.removeAttribute("data-clipboard-text"),h(a,this.options.activeClass),a=null,this.options.text=null},p.prototype.ready=function(){var a=this.htmlBridge.getAttribute("data-clipboard-ready");return"true"===a||a===!0},p.prototype.reposition=function(){if(!a)return!1;var b=j(a);this.htmlBridge.style.top=b.top+"px",this.htmlBridge.style.left=b.left+"px",this.htmlBridge.style.width=b.width+"px",this.htmlBridge.style.height=b.height+"px",this.htmlBridge.style.zIndex=b.zIndex+1,this.setSize(b.width,b.height)},p.dispatch=function(a,b){p.prototype._singleton.receiveEvent(a,b)},p.prototype.on=function(a,b){for(var c=a.toString().split(/\s/g),d=0;dv}aE_J*OX`@ARcled`D42k2>!5>C7Cedy_@C>_bhj`vSb zb?nj1@6ODfnL8ut0P+7sXygwFO=48Zo<<1$dh9P4p%u?Cm!DKN)Pt7o_{$*OnrQ|> zd$~~9-``)@Us`azMq%;BjT?nxsZc7-gT}mn=mhj&-tjNbti}#1%-21u9ayfTMl!9t zo#58YOwX&K_no#oo*lb1^a8V4i#dT`SX@{HM~1#^x?YP0tF+y=EuBX03J3Fk)79VX z(>*qC+SG5Z6b6csO<)B!TU|5UI#V}ncA#EU*9P{nc2^}*HU@#No=B5M)(g7ZDzrV< z=;#oW39e$}lO~aicBgJzev^5t9p{bX?svBspplO5F&enT8XvJ}-KI{XLmO;${c*h3 zm*W^|0Qqzp>e8a7mWqo@-7q3_rSQFK_YgS1RaD9RJF$Yw+=mYzo+}ABVG;hk`1|K5 zzQTjs-`#-||C{ohd2ZRZ+I5$D2Kw{(j)D*@Ak(8Qwumxl0da5t^CuB;sCPAxSgpTi zdXS2bV1eyL=cYVzJ3d>Fcz(~d3~@W~ET=*4x~|QrBR=l5>dZ?IZI|vkFf6}q(?fB) z?O6d6>zyEQ9Z3hR9d-~TY0G#7=U}roYRfetd9Rez;iQ&o@sqGNgN>enZhUTW&V zekiEOXjX;m$3s22+3Zb4RMl_c#(g{clL4J5bL3l7)jjGBdslDugFLByQDu;Ep;RnhFVs7h9av7X z>#7r2wm&}9V9VVGn;r%$-FvsC%Pu_3EQfg)hLtv*fO!;3wa2=OXa0wivnS_bIUb2Y zHxNDWY)C}%o0b`6B&>NJJrw)^MxNW*5~yu89OAg!I<=YXM--~dlr5JU%-eKKm-k#Z zQ0%^}v}rV~5TQR3cpWIAQT42b<@ll>4jPQwQYC5 z;RFM!9L(hEU$xEI-VE7#C&Bf>5E>%A@M;_WC!~r|VDT9=p5K11r^MkEJfV z`{hWlum_N;L+$K(kBLvmq&iDRJ3&Tus=wi*0*Tw*j)E!gH>3nz)3%{pd=Nbj;8{wa zKqs@PZo+<)i`b0Q;Gy>R==aVqJ#^IBA2de($NMRl$N5BFnUn>TpApammOjmEQ}WdO z)WTF@syMZnf5wdnm`F0G@X1tqWHeJHm?Iodut2a#Fd)cHG)4Q z_;Z49ki;^O@bc~{E*-+f*%q5m|zI@ zOM-t!@DqZ!3Em-im*A%)`xU`IC*s!xzaa7&k;??v2&M$r38#~UK@yB4%yjfx;V_4h z)PKw11ou;fkB9&k2~mtFk3ZT#`FO;oWrQRI?=c?ZB+yhU71K0Ei4lzWQH+EPMq(Bt zl7l_RFp|eHQchu%M0_4$ehN<@jz^OS_Bx$SV4T7Pc1fcImgKpx_zKRB=3h-{=d}y( zH1(Z!@tt<*9i4bJsa@8l-)W!3;^aLDiHDeW!lL?Ho!73^rpzy&^gW3B!e_ZFyOY~An6te&N@IewK;r)HcTu8{u zUpa98_VPc%O;nrKuG3F|enGFioC(dV=Iq7{Hs{{rzaN>O?#<;Sgl2*Jop$?=x$_7C ztKHequy%K29%~igz)x+#EU3rTB6nmi_DvRJlMH+}06(y4CXdWgU#WCLsdPdq6~2@n zNT?}?rhJ7i0iB`OfSRAp&&`eiv zab00GG)MZv?a&Y21h`6Z x7)zJKB11J?tUWB&wu-e!#oFUyO@iN7=v;g?38?1AqA4Bz+PnkEe*sSxkJixF*6{!U literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/amsverify.js b/agent-analyer/src/main/resources/static/js/amsverify.js new file mode 100644 index 0000000..ebe9d44 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/amsverify.js @@ -0,0 +1,154 @@ +//验证是否为空或者多空格 + function isNull(str){ + if ( str == "" ) return true; + var regu = "^[ ]+$"; + var re = new RegExp(regu); + return re.test(str); + } + function isChinaOrLett(s){// 判断是否是汉字、字母 + var regu = "^[a-zA-Z\u4e00-\u9fa5]+$"; + var re = new RegExp(regu); + if (re.test(s)) { + return true; + }else{ + return false; + } + } + function isChina(s){// 判断是否是汉字 + var regu = "[\u4e00-\u9fa5]+"; + var re = new RegExp(regu); + if (re.test(s)) { + return true; + }else{ + return false; + } + } + function isCardNo(card) // 是否合法的身份证号 + { + // 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X + var reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/; + if(reg.test(card)) + { + return true; + }else{ + return false + } + } + //合法的IP地址 + function isIP(strIP) { + if (isNull(strIP)) return false; + var re=/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/g // 匹配IP地址的正则表达式 + if(re.test(strIP)) + { + if( RegExp.$1 <256 && RegExp.$2<256 && RegExp.$3<256 && RegExp.$4<256) return true; + } + return false; + } + /* + * 用途:检查输入字符串是否符合正整数格式 输入: s:字符串 返回: 如果通过验证返回true,否则返回false + * + */ + function isNumber( s ){ + var regu = "^[0-9]+$"; + var re = new RegExp(regu); + if (s.search(re) != -1) { + return true; + } else { + return false; + } + } + + /* + 用途:检查输入的起止日期是否正确,启用时间不得早于当前时间, + 且结束如期>=起始日期 + 输入: + startDate:起始日期 + endDate:结束如期 + 返回: + 如果通过验证返回true,否则返回false + + */ + function checkTwoDate( startDate,endDate ) { + var now = new Date();//获得系统当前时间 + //把时间格式转换为字符串 + var year = now.getFullYear(); + var month =(now.getMonth() + 1).toString(); + var day = (now.getDate()).toString(); + if (month.length == 1) { + month = "0" + month; + } + if (day.length == 1) { + day = "0" + day; + } + var dateTime = year+month+day; + //验证 + startDate=startDate.replace("-","").replace("-",""); + endDate=startDate.replace("-","").replace("-",""); + if(startDateendDate) { + return false; + } + return true; + } + /* + 结束如期>=起始日期 + 输入: + startDate:起始日期 + endDate:结束如期 + 返回: + 如果通过验证返回true,否则返回false + + */ + function checkDate( startDate,endDate ) { + startDate=startDate.replace("-","").replace("-",""); + endDate=endDate.replace("-","").replace("-",""); + if( startDate>endDate) { + return false; + } + return true; + } + /* + 用途:检查输入字符串是否只由汉字、字母、数字组成 + 输入: + value:字符串 + 返回: + 如果通过验证返回true,否则返回false + + */ + function isChinaOrNumbOrLett( s ){//判断是否是汉字、字母、数字组成 + var regu = "^[0-9a-zA-Z\u4e00-\u9fa5]+$"; + var re = new RegExp(regu); + if (re.test(s)) { + return true; + }else{ + return false; + } + } + /* + 用途:检查输入字符串是否只由、字母、数字组成 + 输入: + value:字符串 + 返回: + 如果通过验证返回true,否则返回false + + */ + function isNumbOrLett( s ){//判断是否是字母、数字组成 + var regu = "^[0-9a-zA-Z]+$"; + var re = new RegExp(regu); + if (re.test(s)) { + return true; + }else{ + return false; + } + } + /* + 用途:检查输入字符串是否是http://开头 + */ + function isServerUrl(s){ + var patrn=/^(https?|http:\/\/)/i; + var patrn2=/^[-\.\w#%&:?=\/]+$/i; + if (!patrn.exec(s)) return false; + if (!patrn2.exec(s)) return false ; + return true; + } diff --git a/agent-analyer/src/main/resources/static/js/auto-line-number.js b/agent-analyer/src/main/resources/static/js/auto-line-number.js new file mode 100644 index 0000000..f511e52 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/auto-line-number.js @@ -0,0 +1,100 @@ +;(function($){ + var AutoRowsNumbers = function (element, config){ + this.$element = $(element); + this.$group = $('
', { 'class': "textarea-group" }); + this.$ol = $('
', { 'class': 'textarea-rows' }); + this.$wrap = $('
', { 'class': 'textarea-wrap' }); + this.$group.css({ + "width" : this.$element.outerWidth(true) + 'px', + "display" : config.display + }); + this.$ol.css({ + "color" : config.color, + "width" : config.width, + "height" : this.$element.height(), + "font-size" : this.$element.css("font-size"), + "line-height" : this.$element.css("line-height"), + "position" : "absolute", + "overflow" : "hidden", + "margin" : 0, + "padding" : 0, + "text-align": "center", + "font-family": "仿宋" + }); + this.$wrap.css({ + "padding" : ((this.$element.outerHeight() - this.$element.height())/2) + 'px 0', + "background-color" : config.bgColor, + "position" : "absolute", + "width" : config.width, + "height" : this.$element.height() + 'px' + }); + this.$element.css({ + "white-space" : "pre", + "resize": "none", + "margin-left" : (parseInt(config.width) - parseInt(this.$element.css("border-left-width"))) + 'px', + "width": (this.$element.width() - parseInt(config.width)) + 'px' + }); + + } + + AutoRowsNumbers.prototype = { + constructor: AutoRowsNumbers, + + init : function(){ + var that = this; + that.$element.wrap(that.$group); + that.$ol.insertBefore(that.$element); + this.$ol.wrap(that.$wrap) + that.$element.on('keydown',{ that: that }, that.inputText); + that.$element.on('scroll',{ that: that },that.syncScroll); + that.inputText({data:{that:that}}); + }, + + inputText: function(event){ + var that = event.data.that; + + setTimeout(function(){ + var value = that.$element.val(); + value.match(/\n/g) ? that.updateLine(value.match(/\n/g).length+1) : that.updateLine(1); + that.syncScroll({data:{that:that}}); + },0); + }, + + updateLine: function(count){ + var that = this; + that.$element; + that.$ol.html(''); + + for(var i=1;i<=count;i++){ + that.$ol.append("
"+i+"
"); + } + }, + + syncScroll: function(event){ + var that = event.data.that; + that.$ol.children().eq(0).css("margin-top", -(that.$element.scrollTop()) + "px"); + } + } + + $.fn.setTextareaCount = function(option){ + var config = {}; + var option = arguments[0] ? arguments[0] : {}; + config.color = option.color ? option.color : "#FFF"; + config.width = option.width ? option.width : "30px"; + config.bgColor = option.bgColor ? option.bgColor : "#999"; + config.display = option.display ? option.display : "block"; + + return this.each(function () { + var $this = $(this), + data = $this.data('autoRowsNumbers'); + + if (!data){ $this.data('autoRowsNumbers', (data = new AutoRowsNumbers($this, config))); } + + if (typeof option === 'string'){ + return false; + } else { + data.init(); + } + }); + } +})(jQuery) diff --git a/agent-analyer/src/main/resources/static/js/diyfunction.js b/agent-analyer/src/main/resources/static/js/diyfunction.js new file mode 100644 index 0000000..628e4e6 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/diyfunction.js @@ -0,0 +1,1081 @@ +//验证是否为空或者多空格 + function isNull(str){ + if ( str == "" ) return true; + var regu = "^[ ]+$"; + var re = new RegExp(regu); + return re.test(str); + } + function isChinaOrLett(s){// 判断是否是汉字、字母 + var regu = "^[a-zA-Z\u4e00-\u9fa5]+$"; + var re = new RegExp(regu); + if (re.test(s)) { + return true; + }else{ + return false; + } + } + function isChina(s){// 判断是否是汉字 + var regu = "[\u4e00-\u9fa5]+"; + var re = new RegExp(regu); + if (re.test(s)) { + return true; + }else{ + return false; + } + } + function isCardNo(card) // 是否合法的身份证号 + { + // 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X + var reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/; + if(reg.test(card)) + { + return true; + }else{ + return false + } + } + //合法的IP地址 + function isIP(strIP) { + if (isNull(strIP)) return false; + var re=/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/g // 匹配IP地址的正则表达式 + if(re.test(strIP)) + { + if( RegExp.$1 <256 && RegExp.$2<256 && RegExp.$3<256 && RegExp.$4<256) return true; + } + return false; + } + /* + * 用途:检查输入字符串是否符合正整数格式 输入: s:字符串 返回: 如果通过验证返回true,否则返回false + * + */ + function isNumber( s ){ + var regu = "^[0-9]+$"; + var re = new RegExp(regu); + if (s.search(re) != -1) { + return true; + } else { + return false; + } + } + + /* + 用途:检查输入的起止日期是否正确,启用时间不得早于当前时间, + 且结束如期>=起始日期 + 输入: + startDate:起始日期 + endDate:结束如期 + 返回: + 如果通过验证返回true,否则返回false + + */ + function checkTwoDate( startDate,endDate ) { + var now = new Date();//获得系统当前时间 + //把时间格式转换为字符串 + var year = now.getFullYear(); + var month =(now.getMonth() + 1).toString(); + var day = (now.getDate()).toString(); + if (month.length == 1) { + month = "0" + month; + } + if (day.length == 1) { + day = "0" + day; + } + var dateTime = year+month+day; + //验证 + startDate=startDate.replace("-","").replace("-",""); + endDate=startDate.replace("-","").replace("-",""); + if(startDateendDate) { + return false; + } + return true; + } + /* + 结束如期>=起始日期 + 输入: + startDate:起始日期 + endDate:结束如期 + 返回: + 如果通过验证返回true,否则返回false + + */ + function checkDate( startDate,endDate ) { + startDate=startDate.replace("-","").replace("-",""); + endDate=endDate.replace("-","").replace("-",""); + if( startDate>endDate) { + return false; + } + return true; + } + /* + 用途:检查输入字符串是否只由汉字、字母、数字组成 + 输入: + value:字符串 + 返回: + 如果通过验证返回true,否则返回false + + */ + function isChinaOrNumbOrLett( s ){//判断是否是汉字、字母、数字组成 + var regu = "^[0-9a-zA-Z\u4e00-\u9fa5]+$"; + var re = new RegExp(regu); + if (re.test(s)) { + return true; + }else{ + return false; + } + } + /* + 用途:检查输入字符串是否只由、字母、数字组成 + 输入: + value:字符串 + 返回: + 如果通过验证返回true,否则返回false + + */ + function isNumbOrLett( s ){//判断是否是字母、数字组成 + var regu = "^[0-9a-zA-Z]+$"; + var re = new RegExp(regu); + if (re.test(s)) { + return true; + }else{ + return false; + } + } + /* + 用途:检查输入字符串是否是http://开头 + */ + function isServerUrl(s){ + var patrn=/^(https?|http:\/\/)/i; + var patrn2=/^[-\.\w#%&:?=\/]+$/i; + if (!patrn.exec(s)) return false; + if (!patrn2.exec(s)) return false ; + return true; + } + + +/** + * 浏览器复制功能 + */ +function copy(){ + + $('#copy_bar').zclip({ + path: 'ZeroClipboard.swf', + copy: function(){//复制内容 + return $('#code_area').val(); + }, + afterCopy: function(){//复制成功 + $.messager.show({ + title:'复制结果', + msg:'已复制到剪切板。', + timeout:3000, + showType:'slide', + style:{ + left:'', + top:'', + bottom:document.body.scrollTop+document.documentElement.scrollTop + } + + }); + } + }); + +} +/** + * 数据库信息读取cookie,实现记住功能 + */ +function readCookie(){ + $("#dbType").val(getCookie("dbType")); + $("#dbServerIp").val(getCookie("dbServerIp")); + $("#dbServerPort").val(getCookie("dbServerPort")); + $("#dbUserName").val(getCookie("dbUserName")); + $("#dbPassword").val(getCookie("dbPassword")); + $("#dbName").val(getCookie("dbName")); +} + +/** + * 把数据库信息存进cookie,实现记住密码功能 + */ +function setCookie(name,value,expireHours) { +// var cookieString=name+"="+escape(value); + var cookieString=name+"="+value; + //判断是否设置过期时间 + if(expireHours>0){ + var date=new Date(); + date.setTime(date.getTime+expireHours*3600*1000); + cookieString=cookieString+";expires="+date.toGMTString(); + } + document.cookie=cookieString; +} +function setCookies(dbTypeValue,dbServerIpValue,dbServerPortValue,dbUserNameValue,dbPasswordValue,dbNameValue){ + setCookie("dbType",dbTypeValue,12); + setCookie("dbServerIp",dbServerIpValue,12); + setCookie("dbServerPort",dbServerPortValue,12); + setCookie("dbUserName",dbUserNameValue,12); + setCookie("dbPassword",dbPasswordValue,12); + setCookie("dbName",dbNameValue,12); +} + +function getCookie(name){ + var strCookie=document.cookie; + var arrCookie=strCookie.split("; "); + for(var i=0;i1){ + bl= true; + }else{ + layer.alert('通信端口,请输入整数并且在1-65535之间!', -1); + return false; + } + if(isNull(userName)){ + layer.alert('用户名不能为空!', -1); + return false; + } + /* if(isNull(pwd)){ + layer.alert('密码不能为空!', -1); + return false; + } */ + if(isNull(dbName)){ + layer.alert('数据库名不能为空!', -1); + return false; + } + //用户名 + if(isChina(userName)){ + layer.alert('用户名不能为汉字!', -1); + return false; + } + //密码 + if(isChina(pwd)){ + layer.alert('密码不能为汉字!', -1); + return false; + } + //数据库名 + if(isChina(dbName)){ + layer.alert('数据库名不能为汉字!', -1); + return false; + } + return bl; +} +//左移数据 +function moveToLeft() +{ + var rows = $('#checkedTableList').datagrid('getChecked');// 获取所有选中的行 + if(rows.length==0) + { + $.messager.alert('错误',"请至少选择一行数据!",'error'); + return false; + } + for (var i = 0; i < rows.length; i++) + { + //获取自定义table 的中的checkbox值 + var tableName=rows[i].tableName; + $('#tableInfoList').datagrid('insertRow',{ + index: 0, // 索引从0开始 + row: { + tableName: tableName + } + }); + var rowIndex =$("#checkedTableList").datagrid("getRowIndex",rows[i]);//得到索引 + $("#checkedTableList").datagrid("deleteRow",rowIndex); + + + } +} +//点击右移按钮,发生数据变化 +function moveToRight() +{ + var rows = $('#tableInfoList').datagrid('getChecked');// 获取所有选中的行 + if(rows.length==0) + { + $.messager.alert('错误',"请至少选择一行数据!",'error'); + return false; + } + + for (var i = 0; i < rows.length; i++) + { + //获取自定义table 的中的checkbox值 + var tableName=rows[i].tableName; + $('#checkedTableList').datagrid('insertRow',{ + // index: 0, // 索引从0开始 从0开始,就是放在前面,如果不设置,就在后面追加 + row: { + tableName: tableName + } + }); + var rowIndex =$("#tableInfoList").datagrid("getRowIndex",rows[i]);//得到索引 + $("#tableInfoList").datagrid("deleteRow",rowIndex); + + + } + +} +function findObjectInArray(array,nodeText,parentNodeText){ + for ( var i = 0; i < array.length; i++) { + //alert(array[i].columnName+";"+array[i].tableName); + if (nodeText == array[i].columnName && parentNodeText == array[i].tableName) { + return array[i]; + } + } + return false; +} + + + +//重置日期控件格式 +function myformatter(date){ + var y = date.getFullYear(); + var m = date.getMonth()+1; + var d = date.getDate(); + return y+'-'+(m<10?('0'+m):m)+'-'+(d<10?('0'+d):d); +} +function myparser(s){ + if (!s) return new Date(); + var ss = (s.split('-')); + var y = parseInt(ss[0],10); + var m = parseInt(ss[1],10); + var d = parseInt(ss[2],10); + if (!isNaN(y) && !isNaN(m) && !isNaN(d)){ + return new Date(y,m-1,d); + } else { + return new Date(); + } +} + + +function preview(){ + +//$("#preview").click(function(){ + + var userDataType_2=$("#userDataType").combobox('getText')//预置类型 + var startNo_2=$("#startNo").val();//数值开始 + var endNo_2=$("#endNo").val();//数值结束 + var startLength_2=$("#startLength").val();//长度开始 + var endLength_2=$("#endLength").val();//长度结束 + var startDate_2=$('#startDate').datebox('getValue');//开始日期 + var endDate_2=$('#endDate').datebox('getValue');//结束日期 + var diy_2= $.trim($("#diy").val());//自定义字符 + var sql_2= $.trim($("#sql").val());//SQL语句 + var isContain_=$("#isContain_").is(":checked");//判断是否勾选了包含- + var isConnect=$("#isConnect").is(":checked");//判断是否勾选了包含- + + //不管删不删除以前的,都要添加当前页面数据。 + if (userDataType_2 == "数字") { + if(isNull(startNo_2) || isNull(endNo_2)){ + layer.alert('不能为空!', -1); + return false; + }else { + if(!isNumber(startNo_2) || (!isNumber(endNo_2))){ + layer.alert('长度必须为数字!', -1); + return false; + } + } + if ($("#paramsType").combobox('getText')=='随机小数' && !isNumber($.trim($("#numdigits").val()))) { + layer.alert('保留小数位数必须为小于7的数字!', -1); + return false; + } + }else if(userDataType_2=="自定义字符" ) { + if(isNull(diy_2)){ + layer.alert('不能为空!', -1); + return false; + } + }else if(userDataType_2=="SQL语句"){ + if(isNull(sql_2)){ + layer.alert('不能为空!', -1); + return false; + } + }else if(userDataType_2=="日期"){ + if(isNull(startDate_2) || isNull(endDate_2)){ + layer.alert('不能为空!', -1); + return false; + } + }else { + if(isNull(startLength_2) || isNull(endLength_2)){ + layer.alert('不能为空!', -1); + return false; + }else { + if(!isNumber(startLength_2) || (!isNumber(endLength_2))){ + layer.alert('长度必须为数字!', -1); + return false; + } + } + + } + if (!isNull($("#isFormatMessage").html())) { + layer.alert('参数格式不符合规则', -1); + return false; + } +/* $.messager.progress({ + title:"提示框", + text:"正在计算,请稍候..." + }); */ + + $.ajax({ + type : 'post', + url : _path+"/ColumnControl/preView",//测试连接的方法 + datatpye : "script/html", + data : $("#columnInfo").serialize(),//serialize() 方法通过序列化表单值,创建 URL 编码文本字符串。例如:username=admin&password=admin123 + success : function(msgdata) { + $.messager.progress('close'); + //if() + //$.messager.alert('预览结果',msgdata,'info'); + if( JSON.stringify(msgdata).indexOf('Exception:') !=-1){ + $.messager.alert('预览结果',msgdata,'error'); + }else{ + $.messager.alert('测试结果',msgdata,'info'); + } + } + }); +//}) +} + +/** + *点击导入配置 + */ +function openUpload(){ + if(!$("#conn").linkbutton('options').disabled)//获取该按钮的启用、禁用状态 + { + $.messager.alert('提示信息','请先连接数据库并正确选择待操作的数据表!','error'); + return false; + } + $('#upload_win').window('open'); + +} +//在选择表时,提供搜索功能 +function searchTable(){ + var bl= false; + var regu = /^\..*$/;//正整数的正则表达式 + //通信端口 + if(!regu.test($("#searchTableName").attr("value")) ){ + bl= true; + }else{ + layer.alert('输入格式错误', -1); + return false; + } + $('#tableInfoList').datagrid({ + title:'', + method:$("#db_form").attr("method"), + url:_path+"/dbControl/searchTableName", + queryParams:{ + searchTableName:$("#searchTableName").attr("value") + }, + striped : true,//设置为true将交替显示行背景。 + + loadMsg : '数据装载中......', + rownumbers: true, + singleSelect: false, + selectOnCheck: true, + checkOnSelect: true, + columns:[[ + {field:'checkbox',checkbox:true },//显示复选框样式,单独列出来一列,json返回内容不需包括 + {field:'tableName',title:'Table' } + ]], + //onLoadSuccess:function(),判断返回值,但返回值必须是json格式。并且是{'rows':[{'tableName':'连接不成功'}]}这种格式 + /* onLoadSuccess:function(data){ //JSON.stringify(data) 方法把data转为字符串,str.indexOf(str2)!=-1;判断是否包含某字符串 + $.messager.progress('close'); + if(JSON.stringify(data).indexOf("连接不成功") != -1){ + $.messager.alert('连接结果',"连接不成功",'error'); + }else { + $('#win').window('open'); + + } + } */ + }); +} +function paramFormatToDemo(src){ + var regu = "^%(0[0-9]{1,2})d$"; + var re = new RegExp(regu); + var isFormat=re.test(src); + return isFormat; +} + +function join(length){ + var value="1"; + for (var i=1;iKp||t<-Kp}function ft(t){this._target=t.target,this._life=t.life||1e3,this._delay=t.delay||0,this._initialized=!1,this.loop=null!=t.loop&&t.loop,this.gap=t.gap||0,this.easing=t.easing||"Linear",this.onframe=t.onframe,this.ondestroy=t.ondestroy,this.onrestart=t.onrestart,this._pausedTime=0,this._paused=!1}function pt(t){return(t=Math.round(t))<0?0:t>255?255:t}function gt(t){return(t=Math.round(t))<0?0:t>360?360:t}function mt(t){return t<0?0:t>1?1:t}function vt(t){return pt(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100*255:parseInt(t,10))}function yt(t){return mt(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100:parseFloat(t))}function xt(t,e,n){return n<0?n+=1:n>1&&(n-=1),6*n<1?t+(e-t)*n*6:2*n<1?e:3*n<2?t+(e-t)*(2/3-n)*6:t}function _t(t,e,n){return t+(e-t)*n}function wt(t,e,n,i,r){return t[0]=e,t[1]=n,t[2]=i,t[3]=r,t}function bt(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}function Mt(t,e){ug&&bt(ug,e),ug=lg.put(t,ug||e.slice())}function St(t,e){if(t){e=e||[];var n=lg.get(t);if(n)return bt(e,n);var i=(t+="").replace(/ /g,"").toLowerCase();if(i in sg)return bt(e,sg[i]),Mt(t,e),e;if("#"!==i.charAt(0)){var r=i.indexOf("("),o=i.indexOf(")");if(-1!==r&&o+1===i.length){var a=i.substr(0,r),s=i.substr(r+1,o-(r+1)).split(","),l=1;switch(a){case"rgba":if(4!==s.length)return void wt(e,0,0,0,1);l=yt(s.pop());case"rgb":return 3!==s.length?void wt(e,0,0,0,1):(wt(e,vt(s[0]),vt(s[1]),vt(s[2]),l),Mt(t,e),e);case"hsla":return 4!==s.length?void wt(e,0,0,0,1):(s[3]=yt(s[3]),It(s,e),Mt(t,e),e);case"hsl":return 3!==s.length?void wt(e,0,0,0,1):(It(s,e),Mt(t,e),e);default:return}}wt(e,0,0,0,1)}else{if(4===i.length)return(u=parseInt(i.substr(1),16))>=0&&u<=4095?(wt(e,(3840&u)>>4|(3840&u)>>8,240&u|(240&u)>>4,15&u|(15&u)<<4,1),Mt(t,e),e):void wt(e,0,0,0,1);if(7===i.length){var u=parseInt(i.substr(1),16);return u>=0&&u<=16777215?(wt(e,(16711680&u)>>16,(65280&u)>>8,255&u,1),Mt(t,e),e):void wt(e,0,0,0,1)}}}}function It(t,e){var n=(parseFloat(t[0])%360+360)%360/360,i=yt(t[1]),r=yt(t[2]),o=r<=.5?r*(i+1):r+i-r*i,a=2*r-o;return e=e||[],wt(e,pt(255*xt(a,o,n+1/3)),pt(255*xt(a,o,n)),pt(255*xt(a,o,n-1/3)),1),4===t.length&&(e[3]=t[3]),e}function Ct(t){if(t){var e,n,i=t[0]/255,r=t[1]/255,o=t[2]/255,a=Math.min(i,r,o),s=Math.max(i,r,o),l=s-a,u=(s+a)/2;if(0===l)e=0,n=0;else{n=u<.5?l/(s+a):l/(2-s-a);var h=((s-i)/6+l/2)/l,c=((s-r)/6+l/2)/l,d=((s-o)/6+l/2)/l;i===s?e=d-c:r===s?e=1/3+h-d:o===s&&(e=2/3+c-h),e<0&&(e+=1),e>1&&(e-=1)}var f=[360*e,n,u];return null!=t[3]&&f.push(t[3]),f}}function Tt(t,e){var n=St(t);if(n){for(var i=0;i<3;i++)n[i]=e<0?n[i]*(1-e)|0:(255-n[i])*e+n[i]|0,n[i]>255?n[i]=255:t[i]<0&&(n[i]=0);return Lt(n,4===n.length?"rgba":"rgb")}}function Dt(t){var e=St(t);if(e)return((1<<24)+(e[0]<<16)+(e[1]<<8)+ +e[2]).toString(16).slice(1)}function At(t,e,n){if(e&&e.length&&t>=0&&t<=1){n=n||[];var i=t*(e.length-1),r=Math.floor(i),o=Math.ceil(i),a=e[r],s=e[o],l=i-r;return n[0]=pt(_t(a[0],s[0],l)),n[1]=pt(_t(a[1],s[1],l)),n[2]=pt(_t(a[2],s[2],l)),n[3]=mt(_t(a[3],s[3],l)),n}}function kt(t,e,n){if(e&&e.length&&t>=0&&t<=1){var i=t*(e.length-1),r=Math.floor(i),o=Math.ceil(i),a=St(e[r]),s=St(e[o]),l=i-r,u=Lt([pt(_t(a[0],s[0],l)),pt(_t(a[1],s[1],l)),pt(_t(a[2],s[2],l)),mt(_t(a[3],s[3],l))],"rgba");return n?{color:u,leftIndex:r,rightIndex:o,value:i}:u}}function Pt(t,e){if((t=St(t))&&null!=e)return t[3]=mt(e),Lt(t,"rgba")}function Lt(t,e){if(t&&t.length){var n=t[0]+","+t[1]+","+t[2];return"rgba"!==e&&"hsva"!==e&&"hsla"!==e||(n+=","+t[3]),e+"("+n+")"}}function Ot(t,e){return t[e]}function zt(t,e,n){t[e]=n}function Et(t,e,n){return(e-t)*n+t}function Nt(t,e,n){return n>.5?e:t}function Rt(t,e,n,i,r){var o=t.length;if(1==r)for(s=0;sr)t.length=r;else for(a=i;a=0&&!(m[n]<=e);n--);n=Math.min(n,u-2)}else{for(n=k;ne);n++);n=Math.min(n-1,u-2)}k=n,P=e;var i=m[n+1]-m[n];if(0!==i)if(I=(e-m[n])/i,l)if(T=v[n],C=v[0===n?n:n-1],D=v[n>u-2?u-1:n+1],A=v[n>u-3?u-1:n+2],d)Ft(C,T,D,A,I,I*I,I*I*I,a(t,r),g);else{if(f)o=Ft(C,T,D,A,I,I*I,I*I*I,L,1),o=Wt(L);else{if(p)return Nt(T,D,I);o=Ht(C,T,D,A,I,I*I,I*I*I)}s(t,r,o)}else if(d)Rt(v[n],v[n+1],I,a(t,r),g);else{var o;if(f)Rt(v[n],v[n+1],I,L,1),o=Wt(L);else{if(p)return Nt(v[n],v[n+1],I);o=Et(v[n],v[n+1],I)}s(t,r,o)}},ondestroy:n});return e&&"spline"!==e&&(O.easing=e),O}}}function Xt(t,e,n,i){n<0&&(t+=n,n=-n),i<0&&(e+=i,i=-i),this.x=t,this.y=e,this.width=n,this.height=i}function jt(t){for(var e=0;t>=Ig;)e|=1&t,t>>=1;return t+e}function Yt(t,e,n,i){var r=e+1;if(r===n)return 1;if(i(t[r++],t[e])<0){for(;r=0;)r++;return r-e}function qt(t,e,n){for(n--;e>>1])<0?l=o:s=o+1;var u=i-s;switch(u){case 3:t[s+3]=t[s+2];case 2:t[s+2]=t[s+1];case 1:t[s+1]=t[s];break;default:for(;u>0;)t[s+u]=t[s+u-1],u--}t[s]=a}}function Kt(t,e,n,i,r,o){var a=0,s=0,l=1;if(o(t,e[n+r])>0){for(s=i-r;l0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}else{for(s=r+1;ls&&(l=s);var u=a;a=r-l,l=r-u}for(a++;a>>1);o(t,e[n+h])>0?a=h+1:l=h}return l}function Qt(t,e,n,i,r,o){var a=0,s=0,l=1;if(o(t,e[n+r])<0){for(s=r+1;ls&&(l=s);var u=a;a=r-l,l=r-u}else{for(s=i-r;l=0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}for(a++;a>>1);o(t,e[n+h])<0?l=h:a=h+1}return l}function Jt(t,e){function n(n){var s=o[n],u=a[n],h=o[n+1],c=a[n+1];a[n]=u+c,n===l-3&&(o[n+1]=o[n+2],a[n+1]=a[n+2]),l--;var d=Qt(t[h],t,s,u,0,e);s+=d,0!==(u-=d)&&0!==(c=Kt(t[s+u-1],t,h,c,c-1,e))&&(u<=c?i(s,u,h,c):r(s,u,h,c))}function i(n,i,r,o){var a=0;for(a=0;a=Cg||f>=Cg);if(p)break;g<0&&(g=0),g+=2}if((s=g)<1&&(s=1),1===i){for(a=0;a=0;a--)t[f+a]=t[d+a];if(0===i){v=!0;break}}if(t[c--]=u[h--],1==--o){v=!0;break}if(0!=(m=o-Kt(t[l],u,0,o,o-1,e))){for(o-=m,f=(c-=m)+1,d=(h-=m)+1,a=0;a=Cg||m>=Cg);if(v)break;p<0&&(p=0),p+=2}if((s=p)<1&&(s=1),1===o){for(f=(c-=i)+1,d=(l-=i)+1,a=i-1;a>=0;a--)t[f+a]=t[d+a];t[c]=u[h]}else{if(0===o)throw new Error;for(d=c-(o-1),a=0;a=0;a--)t[f+a]=t[d+a];t[c]=u[h]}else for(d=c-(o-1),a=0;a1;){var t=l-2;if(t>=1&&a[t-1]<=a[t]+a[t+1]||t>=2&&a[t-2]<=a[t]+a[t-1])a[t-1]a[t+1])break;n(t)}},this.forceMergeRuns=function(){for(;l>1;){var t=l-2;t>0&&a[t-1]s&&(l=s),$t(t,n,n+l,n+o,e),o=l}a.pushRun(n,o),a.mergeRuns(),r-=o,n+=o}while(0!==r);a.forceMergeRuns()}}function ee(t,e){return t.zlevel===e.zlevel?t.z===e.z?t.z2-e.z2:t.z-e.z:t.zlevel-e.zlevel}function ne(t,e,n){var i=null==e.x?0:e.x,r=null==e.x2?1:e.x2,o=null==e.y?0:e.y,a=null==e.y2?0:e.y2;return e.global||(i=i*n.width+n.x,r=r*n.width+n.x,o=o*n.height+n.y,a=a*n.height+n.y),i=isNaN(i)?0:i,r=isNaN(r)?1:r,o=isNaN(o)?0:o,a=isNaN(a)?0:a,t.createLinearGradient(i,o,r,a)}function ie(t,e,n){var i=n.width,r=n.height,o=Math.min(i,r),a=null==e.x?.5:e.x,s=null==e.y?.5:e.y,l=null==e.r?.5:e.r;return e.global||(a=a*i+n.x,s=s*r+n.y,l*=o),t.createRadialGradient(a,s,0,a,s,l)}function re(){return!1}function oe(t,e,n){var i=Op(),r=e.getWidth(),o=e.getHeight(),a=i.style;return a&&(a.position="absolute",a.left=0,a.top=0,a.width=r+"px",a.height=o+"px",i.setAttribute("data-zr-dom-id",t)),i.width=r*n,i.height=o*n,i}function ae(t){if("string"==typeof t){var e=Bg.get(t);return e&&e.image}return t}function se(t,e,n,i,r){if(t){if("string"==typeof t){if(e&&e.__zrImageSrc===t||!n)return e;var o=Bg.get(t),a={hostEl:n,cb:i,cbPayload:r};return o?!ue(e=o.image)&&o.pending.push(a):(!e&&(e=new Image),e.onload=le,Bg.put(t,e.__cachedImgObj={image:e,pending:[a]}),e.src=e.__zrImageSrc=t),e}return t}return e}function le(){var t=this.__cachedImgObj;this.onload=this.__cachedImgObj=null;for(var e=0;eHg&&(Fg=0,Vg={}),Fg++,Vg[n]=r,r}function ce(t,e,n,i,r,o,a){return o?fe(t,e,n,i,r,o,a):de(t,e,n,i,r,a)}function de(t,e,n,i,r,o){var a=Me(t,e,r,o),s=he(t,e);r&&(s+=r[1]+r[3]);var l=a.outerHeight,u=new Xt(pe(0,s,n),ge(0,l,i),s,l);return u.lineHeight=a.lineHeight,u}function fe(t,e,n,i,r,o,a){var s=Se(t,{rich:o,truncate:a,font:e,textAlign:n,textPadding:r}),l=s.outerWidth,u=s.outerHeight;return new Xt(pe(0,l,n),ge(0,u,i),l,u)}function pe(t,e,n){return"right"===n?t-=e:"center"===n&&(t-=e/2),t}function ge(t,e,n){return"middle"===n?t-=e/2:"bottom"===n&&(t-=e),t}function me(t,e,n){var i=e.x,r=e.y,o=e.height,a=e.width,s=o/2,l="left",u="top";switch(t){case"left":i-=n,r+=s,l="right",u="middle";break;case"right":i+=n+a,r+=s,u="middle";break;case"top":i+=a/2,r-=n,l="center",u="bottom";break;case"bottom":i+=a/2,r+=o+n,l="center";break;case"inside":i+=a/2,r+=s,l="center",u="middle";break;case"insideLeft":i+=n,r+=s,u="middle";break;case"insideRight":i+=a-n,r+=s,l="right",u="middle";break;case"insideTop":i+=a/2,r+=n,l="center";break;case"insideBottom":i+=a/2,r+=o-n,l="center",u="bottom";break;case"insideTopLeft":i+=n,r+=n;break;case"insideTopRight":i+=a-n,r+=n,l="right";break;case"insideBottomLeft":i+=n,r+=o-n,u="bottom";break;case"insideBottomRight":i+=a-n,r+=o-n,l="right",u="bottom"}return{x:i,y:r,textAlign:l,textVerticalAlign:u}}function ve(t,e,n,i,r){if(!e)return"";var o=(t+"").split("\n");r=ye(e,n,i,r);for(var a=0,s=o.length;a=a;l++)s-=a;var u=he(n);return u>s&&(n="",u=0),s=t-u,i.ellipsis=n,i.ellipsisWidth=u,i.contentWidth=s,i.containerWidth=t,i}function xe(t,e){var n=e.containerWidth,i=e.font,r=e.contentWidth;if(!n)return"";var o=he(t,i);if(o<=n)return t;for(var a=0;;a++){if(o<=r||a>=e.maxIterations){t+=e.ellipsis;break}var s=0===a?_e(t,r,e.ascCharWidth,e.cnCharWidth):o>0?Math.floor(t.length*r/o):0;o=he(t=t.substr(0,s),i)}return""===t&&(t=e.placeholder),t}function _e(t,e,n,i){for(var r=0,o=0,a=t.length;ol)t="",o=[];else if(null!=u)for(var h=ye(u-(n?n[1]+n[3]:0),e,i.ellipsis,{minChar:i.minChar,placeholder:i.placeholder}),c=0,d=o.length;cr&&Ie(n,t.substring(r,o)),Ie(n,i[2],i[1]),r=Gg.lastIndex}rf)return{lines:[],width:0,height:0};P.textWidth=he(P.text,_);var b=y.textWidth,M=null==b||"auto"===b;if("string"==typeof b&&"%"===b.charAt(b.length-1))P.percentWidth=b,u.push(P),b=0;else{if(M){b=P.textWidth;var S=y.textBackgroundColor,I=S&&S.image;I&&ue(I=ae(I))&&(b=Math.max(b,I.width*w/I.height))}var C=x?x[1]+x[3]:0;b+=C;var A=null!=d?d-m:null;null!=A&&Al&&(n*=l/(c=n+i),i*=l/c),r+o>l&&(r*=l/(c=r+o),o*=l/c),i+r>u&&(i*=u/(c=i+r),r*=u/c),n+o>u&&(n*=u/(c=n+o),o*=u/c),t.moveTo(a+n,s),t.lineTo(a+l-i,s),0!==i&&t.arc(a+l-i,s+i,i,-Math.PI/2,0),t.lineTo(a+l,s+u-r),0!==r&&t.arc(a+l-r,s+u-r,r,0,Math.PI/2),t.lineTo(a+o,s+u),0!==o&&t.arc(a+o,s+u-o,o,Math.PI/2,Math.PI),t.lineTo(a,s+n),0!==n&&t.arc(a+n,s+n,n,Math.PI,1.5*Math.PI)}function De(t){return Ae(t),d(t.rich,Ae),t}function Ae(t){if(t){t.font=Ce(t);var e=t.textAlign;"middle"===e&&(e="center"),t.textAlign=null==e||Ug[e]?e:"left";var n=t.textVerticalAlign||t.textBaseline;"center"===n&&(n="middle"),t.textVerticalAlign=null==n||Xg[n]?n:"top",t.textPadding&&(t.textPadding=k(t.textPadding))}}function ke(t,e,n,i,r){i.rich?Le(t,e,n,i,r):Pe(t,e,n,i,r)}function Pe(t,e,n,i,r){var o=Fe(e,"font",i.font||Wg),a=i.textPadding,s=t.__textCotentBlock;s&&!t.__dirty||(s=t.__textCotentBlock=Me(n,o,a,i.truncate));var l=s.outerHeight,u=s.lines,h=s.lineHeight,c=Ve(l,i,r),d=c.baseX,f=c.baseY,p=c.textAlign,g=c.textVerticalAlign;ze(e,i,r,d,f);var m=ge(f,l,g),v=d,y=m,x=Ne(i);if(x||a){var _=he(n,o);a&&(_+=a[1]+a[3]);var w=pe(d,_,p);x&&Re(t,e,i,w,m,_,l),a&&(v=Ze(d,p,a),y+=a[0])}Fe(e,"textAlign",p||"left"),Fe(e,"textBaseline","middle"),Fe(e,"shadowBlur",i.textShadowBlur||0),Fe(e,"shadowColor",i.textShadowColor||"transparent"),Fe(e,"shadowOffsetX",i.textShadowOffsetX||0),Fe(e,"shadowOffsetY",i.textShadowOffsetY||0),y+=h/2;var b=i.textStrokeWidth,M=He(i.textStroke,b),S=Ge(i.textFill);M&&(Fe(e,"lineWidth",b),Fe(e,"strokeStyle",M)),S&&Fe(e,"fillStyle",S);for(var I=0;I=0&&"right"===(_=b[A]).textAlign;)Ee(t,e,_,i,S,v,D,"right"),I-=_.width,D-=_.width,A--;for(T+=(o-(T-m)-(y-D)-I)/2;C<=A;)Ee(t,e,_=b[C],i,S,v,T+_.width/2,"center"),T+=_.width,C++;v+=S}}function ze(t,e,n,i,r){if(n&&e.textRotation){var o=e.textOrigin;"center"===o?(i=n.width/2+n.x,r=n.height/2+n.y):o&&(i=o[0]+n.x,r=o[1]+n.y),t.translate(i,r),t.rotate(-e.textRotation),t.translate(-i,-r)}}function Ee(t,e,n,i,r,o,a,s){var l=i.rich[n.styleName]||{},u=n.textVerticalAlign,h=o+r/2;"top"===u?h=o+n.height/2:"bottom"===u&&(h=o+r-n.height/2),!n.isLineHolder&&Ne(l)&&Re(t,e,l,"right"===s?a-n.width:"center"===s?a-n.width/2:a,h-n.height/2,n.width,n.height);var c=n.textPadding;c&&(a=Ze(a,s,c),h-=n.height/2-c[2]-n.textHeight/2),Fe(e,"shadowBlur",D(l.textShadowBlur,i.textShadowBlur,0)),Fe(e,"shadowColor",l.textShadowColor||i.textShadowColor||"transparent"),Fe(e,"shadowOffsetX",D(l.textShadowOffsetX,i.textShadowOffsetX,0)),Fe(e,"shadowOffsetY",D(l.textShadowOffsetY,i.textShadowOffsetY,0)),Fe(e,"textAlign",s),Fe(e,"textBaseline","middle"),Fe(e,"font",n.font||Wg);var d=He(l.textStroke||i.textStroke,p),f=Ge(l.textFill||i.textFill),p=T(l.textStrokeWidth,i.textStrokeWidth);d&&(Fe(e,"lineWidth",p),Fe(e,"strokeStyle",d),e.strokeText(n.text,a,h)),f&&(Fe(e,"fillStyle",f),e.fillText(n.text,a,h))}function Ne(t){return t.textBackgroundColor||t.textBorderWidth&&t.textBorderColor}function Re(t,e,n,i,r,o,a){var s=n.textBackgroundColor,l=n.textBorderWidth,u=n.textBorderColor,h=_(s);if(Fe(e,"shadowBlur",n.textBoxShadowBlur||0),Fe(e,"shadowColor",n.textBoxShadowColor||"transparent"),Fe(e,"shadowOffsetX",n.textBoxShadowOffsetX||0),Fe(e,"shadowOffsetY",n.textBoxShadowOffsetY||0),h||l&&u){e.beginPath();var c=n.textBorderRadius;c?Te(e,{x:i,y:r,width:o,height:a,r:c}):e.rect(i,r,o,a),e.closePath()}if(h)Fe(e,"fillStyle",s),e.fill();else if(w(s)){var d=s.image;(d=se(d,null,t,Be,s))&&ue(d)&&e.drawImage(d,i,r,o,a)}l&&u&&(Fe(e,"lineWidth",l),Fe(e,"strokeStyle",u),e.stroke())}function Be(t,e){e.image=t}function Ve(t,e,n){var i=e.x||0,r=e.y||0,o=e.textAlign,a=e.textVerticalAlign;if(n){var s=e.textPosition;if(s instanceof Array)i=n.x+We(s[0],n.width),r=n.y+We(s[1],n.height);else{var l=me(s,n,e.textDistance);i=l.x,r=l.y,o=o||l.textAlign,a=a||l.textVerticalAlign}var u=e.textOffset;u&&(i+=u[0],r+=u[1])}return{baseX:i,baseY:r,textAlign:o,textVerticalAlign:a}}function Fe(t,e,n){return t[e]=Ag(t,e,n),t[e]}function He(t,e){return null==t||e<=0||"transparent"===t||"none"===t?null:t.image||t.colorStops?"#000":t}function Ge(t){return null==t||"none"===t?null:t.image||t.colorStops?"#000":t}function We(t,e){return"string"==typeof t?t.lastIndexOf("%")>=0?parseFloat(t)/100*e:parseFloat(t):t}function Ze(t,e,n){return"right"===e?t-n[1]:"center"===e?t+n[3]/2-n[1]/2:t+n[3]}function Ue(t,e){return null!=t&&(t||e.textBackgroundColor||e.textBorderWidth&&e.textBorderColor||e.textPadding)}function Xe(t){t=t||{},_g.call(this,t);for(var e in t)t.hasOwnProperty(e)&&"style"!==e&&(this[e]=t[e]);this.style=new Pg(t.style,this),this._rect=null,this.__clipPaths=[]}function je(t){Xe.call(this,t)}function Ye(t){return parseInt(t,10)}function qe(t){return!!t&&(!!t.__builtin__||"function"==typeof t.resize&&"function"==typeof t.refresh)}function $e(t,e,n){return qg.copy(t.getBoundingRect()),t.transform&&qg.applyTransform(t.transform),$g.width=e,$g.height=n,!qg.intersect($g)}function Ke(t,e){if(t==e)return!1;if(!t||!e||t.length!==e.length)return!0;for(var n=0;n=0){var r="touchend"!=i?e.targetTouches[0]:e.changedTouches[0];r&&en(t,r,e,n)}else en(t,e,e,n),e.zrDelta=e.wheelDelta?e.wheelDelta/120:-(e.detail||0)/3;var o=e.button;return null==e.which&&void 0!==o&&Jg.test(e.type)&&(e.which=1&o?1:2&o?3:4&o?2:0),e}function on(t,e,n){Qg?t.addEventListener(e,n):t.attachEvent("on"+e,n)}function an(t,e,n){Qg?t.removeEventListener(e,n):t.detachEvent("on"+e,n)}function sn(t){return t.which>1}function ln(t){var e=t[1][0]-t[0][0],n=t[1][1]-t[0][1];return Math.sqrt(e*e+n*n)}function un(t){return[(t[0][0]+t[1][0])/2,(t[0][1]+t[1][1])/2]}function hn(t){return"mousewheel"===t&&bp.browser.firefox?"DOMMouseScroll":t}function cn(t,e,n){var i=t._gestureMgr;"start"===n&&i.clear();var r=i.recognize(e,t.handler.findHover(e.zrX,e.zrY,null).target,t.dom);if("end"===n&&i.clear(),r){var o=r.type;e.gestureEvent=o,t.handler.dispatchToElement({target:r.target},o,r.event)}}function dn(t){t._touching=!0,clearTimeout(t._touchTimer),t._touchTimer=setTimeout(function(){t._touching=!1},700)}function fn(t){var e=t.pointerType;return"pen"===e||"touch"===e}function pn(t){function e(t,e){return function(){if(!e._touching)return t.apply(e,arguments)}}d(om,function(e){t._handlers[e]=m(lm[e],t)}),d(sm,function(e){t._handlers[e]=m(lm[e],t)}),d(rm,function(n){t._handlers[n]=e(lm[n],t)})}function gn(t){function e(e,n){d(e,function(e){on(t,hn(e),n._handlers[e])},n)}Zp.call(this),this.dom=t,this._touching=!1,this._touchTimer,this._gestureMgr=new nm,this._handlers={},pn(this),bp.pointerEventsSupported?e(sm,this):(bp.touchEventsSupported&&e(om,this),e(rm,this))}function mn(t,e){var n=new fm(_p(),t,e);return dm[n.id]=n,n}function vn(t,e){cm[t]=e}function yn(t){delete dm[t]}function xn(t){return t instanceof Array?t:null==t?[]:[t]}function _n(t,e,n){if(t){t[e]=t[e]||{},t.emphasis=t.emphasis||{},t.emphasis[e]=t.emphasis[e]||{};for(var i=0,r=n.length;i=n.length&&n.push({option:t})}}),n}function Sn(t){var e=N();gm(t,function(t,n){var i=t.exist;i&&e.set(i.id,t)}),gm(t,function(t,n){var i=t.option;P(!i||null==i.id||!e.get(i.id)||e.get(i.id)===t,"id duplicates: "+(i&&i.id)),i&&null!=i.id&&e.set(i.id,t),!t.keyInfo&&(t.keyInfo={})}),gm(t,function(t,n){var i=t.exist,r=t.option,o=t.keyInfo;if(mm(r)){if(o.name=null!=r.name?r.name+"":i?i.name:ym+n,i)o.id=i.id;else if(null!=r.id)o.id=r.id+"";else{var a=0;do{o.id="\0"+o.name+"\0"+a++}while(e.get(o.id))}e.set(o.id,t)}})}function In(t){var e=t.name;return!(!e||!e.indexOf(ym))}function Cn(t){return mm(t)&&t.id&&0===(t.id+"").indexOf("\0_ec_\0")}function Tn(t,e){return null!=e.dataIndexInside?e.dataIndexInside:null!=e.dataIndex?y(e.dataIndex)?f(e.dataIndex,function(e){return t.indexOfRawIndex(e)}):t.indexOfRawIndex(e.dataIndex):null!=e.name?y(e.name)?f(e.name,function(e){return t.indexOfName(e)}):t.indexOfName(e.name):void 0}function Dn(){var t="__\0ec_inner_"+_m+++"_"+Math.random().toFixed(5);return function(e){return e[t]||(e[t]={})}}function An(t,e,n){if(_(e)){var i={};i[e+"Index"]=0,e=i}var r=n&&n.defaultMainType;!r||kn(e,r+"Index")||kn(e,r+"Id")||kn(e,r+"Name")||(e[r+"Index"]=0);var o={};return gm(e,function(i,r){var i=e[r];if("dataIndex"!==r&&"dataIndexInside"!==r){var a=r.match(/^(\w+)(Index|Id|Name)$/)||[],s=a[1],u=(a[2]||"").toLowerCase();if(!(!s||!u||null==i||"index"===u&&"none"===i||n&&n.includeMainTypes&&l(n.includeMainTypes,s)<0)){var h={mainType:s};"index"===u&&"all"===i||(h[u]=i);var c=t.queryComponents(h);o[s+"Models"]=c,o[s+"Model"]=c[0]}}else o[r]=i}),o}function kn(t,e){return t&&t.hasOwnProperty(e)}function Pn(t,e,n){t.setAttribute?t.setAttribute(e,n):t[e]=n}function Ln(t,e){return t.getAttribute?t.getAttribute(e):t[e]}function On(t){var e={main:"",sub:""};return t&&(t=t.split(wm),e.main=t[0]||"",e.sub=t[1]||""),e}function zn(t){P(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(t),'componentType "'+t+'" illegal')}function En(t,e){t.$constructor=t,t.extend=function(t){var e=this,n=function(){t.$constructor?t.$constructor.apply(this,arguments):e.apply(this,arguments)};return o(n.prototype,t),n.extend=this.extend,n.superCall=Rn,n.superApply=Bn,u(n,this),n.superClass=e,n}}function Nn(t){var e=["__\0is_clz",Mm++,Math.random().toFixed(3)].join("_");t.prototype[e]=!0,t.isInstance=function(t){return!(!t||!t[e])}}function Rn(t,e){var n=A(arguments,2);return this.superClass.prototype[e].apply(t,n)}function Bn(t,e,n){return this.superClass.prototype[e].apply(t,n)}function Vn(t,e){function n(t){var e=i[t.main];return e&&e[bm]||((e=i[t.main]={})[bm]=!0),e}e=e||{};var i={};if(t.registerClass=function(t,e){return e&&(zn(e),(e=On(e)).sub?e.sub!==bm&&(n(e)[e.sub]=t):i[e.main]=t),t},t.getClass=function(t,e,n){var r=i[t];if(r&&r[bm]&&(r=e?r[e]:null),n&&!r)throw new Error(e?"Component "+t+"."+(e||"")+" not exists. Load it first.":t+".type should be specified.");return r},t.getClassesByMainType=function(t){t=On(t);var e=[],n=i[t.main];return n&&n[bm]?d(n,function(t,n){n!==bm&&e.push(t)}):e.push(n),e},t.hasClass=function(t){return t=On(t),!!i[t.main]},t.getAllClassMainTypes=function(){var t=[];return d(i,function(e,n){t.push(n)}),t},t.hasSubTypes=function(t){t=On(t);var e=i[t.main];return e&&e[bm]},t.parseClassType=On,e.registerWhenExtend){var r=t.extend;r&&(t.extend=function(e){var n=r.call(this,e);return t.registerClass(n,e.type)})}return t}function Fn(t){return t>-Pm&&tPm||t<-Pm}function Gn(t,e,n,i,r){var o=1-r;return o*o*(o*t+3*r*e)+r*r*(r*i+3*o*n)}function Wn(t,e,n,i,r){var o=1-r;return 3*(((e-t)*o+2*(n-e)*r)*o+(i-n)*r*r)}function Zn(t,e,n,i,r,o){var a=i+3*(e-n)-t,s=3*(n-2*e+t),l=3*(e-t),u=t-r,h=s*s-3*a*l,c=s*l-9*a*u,d=l*l-3*s*u,f=0;if(Fn(h)&&Fn(c))Fn(s)?o[0]=0:(S=-l/s)>=0&&S<=1&&(o[f++]=S);else{var p=c*c-4*h*d;if(Fn(p)){var g=c/h,m=-g/2;(S=-s/a+g)>=0&&S<=1&&(o[f++]=S),m>=0&&m<=1&&(o[f++]=m)}else if(p>0){var v=km(p),y=h*s+1.5*a*(-c+v),x=h*s+1.5*a*(-c-v);(S=(-s-((y=y<0?-Am(-y,zm):Am(y,zm))+(x=x<0?-Am(-x,zm):Am(x,zm))))/(3*a))>=0&&S<=1&&(o[f++]=S)}else{var _=(2*h*s-3*a*c)/(2*km(h*h*h)),w=Math.acos(_)/3,b=km(h),M=Math.cos(w),S=(-s-2*b*M)/(3*a),m=(-s+b*(M+Om*Math.sin(w)))/(3*a),I=(-s+b*(M-Om*Math.sin(w)))/(3*a);S>=0&&S<=1&&(o[f++]=S),m>=0&&m<=1&&(o[f++]=m),I>=0&&I<=1&&(o[f++]=I)}}return f}function Un(t,e,n,i,r){var o=6*n-12*e+6*t,a=9*e+3*i-3*t-9*n,s=3*e-3*t,l=0;if(Fn(a))Hn(o)&&(c=-s/o)>=0&&c<=1&&(r[l++]=c);else{var u=o*o-4*a*s;if(Fn(u))r[0]=-o/(2*a);else if(u>0){var h=km(u),c=(-o+h)/(2*a),d=(-o-h)/(2*a);c>=0&&c<=1&&(r[l++]=c),d>=0&&d<=1&&(r[l++]=d)}}return l}function Xn(t,e,n,i,r,o){var a=(e-t)*r+t,s=(n-e)*r+e,l=(i-n)*r+n,u=(s-a)*r+a,h=(l-s)*r+s,c=(h-u)*r+u;o[0]=t,o[1]=a,o[2]=u,o[3]=c,o[4]=c,o[5]=h,o[6]=l,o[7]=i}function jn(t,e,n,i,r,o,a,s,l,u,h){var c,d,f,p,g,m=.005,v=1/0;Em[0]=l,Em[1]=u;for(var y=0;y<1;y+=.05)Nm[0]=Gn(t,n,r,a,y),Nm[1]=Gn(e,i,o,s,y),(p=Hp(Em,Nm))=0&&p=0&&c<=1&&(r[l++]=c);else{var u=a*a-4*o*s;if(Fn(u))(c=-a/(2*o))>=0&&c<=1&&(r[l++]=c);else if(u>0){var h=km(u),c=(-a+h)/(2*o),d=(-a-h)/(2*o);c>=0&&c<=1&&(r[l++]=c),d>=0&&d<=1&&(r[l++]=d)}}return l}function Kn(t,e,n){var i=t+n-2*e;return 0===i?.5:(t-e)/i}function Qn(t,e,n,i,r){var o=(e-t)*i+t,a=(n-e)*i+e,s=(a-o)*i+o;r[0]=t,r[1]=o,r[2]=s,r[3]=s,r[4]=a,r[5]=n}function Jn(t,e,n,i,r,o,a,s,l){var u,h=.005,c=1/0;Em[0]=a,Em[1]=s;for(var d=0;d<1;d+=.05)Nm[0]=Yn(t,n,r,d),Nm[1]=Yn(e,i,o,d),(m=Hp(Em,Nm))=0&&m1e-4)return s[0]=t-n,s[1]=e-i,l[0]=t+n,void(l[1]=e+i);if(Wm[0]=Hm(r)*n+t,Wm[1]=Fm(r)*i+e,Zm[0]=Hm(o)*n+t,Zm[1]=Fm(o)*i+e,u(s,Wm,Zm),h(l,Wm,Zm),(r%=Gm)<0&&(r+=Gm),(o%=Gm)<0&&(o+=Gm),r>o&&!a?o+=Gm:rr&&(Um[0]=Hm(f)*n+t,Um[1]=Fm(f)*i+e,u(s,Um,s),h(l,Um,l))}function oi(t,e,n,i,r,o,a){if(0===r)return!1;var s=r,l=0,u=t;if(a>e+s&&a>i+s||at+s&&o>n+s||oe+c&&h>i+c&&h>o+c&&h>s+c||ht+c&&u>n+c&&u>r+c&&u>a+c||ue+u&&l>i+u&&l>o+u||lt+u&&s>n+u&&s>r+u||sn||h+ur&&(r+=lv);var d=Math.atan2(l,s);return d<0&&(d+=lv),d>=i&&d<=r||d+lv>=i&&d+lv<=r}function hi(t,e,n,i,r,o){if(o>e&&o>i||or?a:0}function ci(t,e){return Math.abs(t-e)e&&u>i&&u>o&&u>s||u1&&di(),c=Gn(e,i,o,s,fv[0]),p>1&&(d=Gn(e,i,o,s,fv[1]))),2==p?me&&s>i&&s>o||s=0&&u<=1){for(var h=0,c=Yn(e,i,o,u),d=0;dn||s<-n)return 0;u=Math.sqrt(n*n-s*s);dv[0]=-u,dv[1]=u;var l=Math.abs(i-r);if(l<1e-4)return 0;if(l%hv<1e-4){i=0,r=hv;p=o?1:-1;return a>=dv[0]+t&&a<=dv[1]+t?p:0}if(o){var u=i;i=li(r),r=li(u)}else i=li(i),r=li(r);i>r&&(r+=hv);for(var h=0,c=0;c<2;c++){var d=dv[c];if(d+t>a){var f=Math.atan2(s,d),p=o?1:-1;f<0&&(f=hv+f),(f>=i&&f<=r||f+hv>=i&&f+hv<=r)&&(f>Math.PI/2&&f<1.5*Math.PI&&(p=-p),h+=p)}}return h}function mi(t,e,n,i,r){for(var o=0,a=0,s=0,l=0,u=0,h=0;h1&&(n||(o+=hi(a,s,l,u,i,r))),1==h&&(l=a=t[h],u=s=t[h+1]),c){case uv.M:a=l=t[h++],s=u=t[h++];break;case uv.L:if(n){if(oi(a,s,t[h],t[h+1],e,i,r))return!0}else o+=hi(a,s,t[h],t[h+1],i,r)||0;a=t[h++],s=t[h++];break;case uv.C:if(n){if(ai(a,s,t[h++],t[h++],t[h++],t[h++],t[h],t[h+1],e,i,r))return!0}else o+=fi(a,s,t[h++],t[h++],t[h++],t[h++],t[h],t[h+1],i,r)||0;a=t[h++],s=t[h++];break;case uv.Q:if(n){if(si(a,s,t[h++],t[h++],t[h],t[h+1],e,i,r))return!0}else o+=pi(a,s,t[h++],t[h++],t[h],t[h+1],i,r)||0;a=t[h++],s=t[h++];break;case uv.A:var d=t[h++],f=t[h++],p=t[h++],g=t[h++],m=t[h++],v=t[h++],y=(t[h++],1-t[h++]),x=Math.cos(m)*p+d,_=Math.sin(m)*g+f;h>1?o+=hi(a,s,x,_,i,r):(l=x,u=_);var w=(i-d)*g/p+d;if(n){if(ui(d,f,g,m,m+v,y,e,w,r))return!0}else o+=gi(d,f,g,m,m+v,y,w,r);a=Math.cos(m+v)*p+d,s=Math.sin(m+v)*g+f;break;case uv.R:l=a=t[h++],u=s=t[h++];var x=l+t[h++],_=u+t[h++];if(n){if(oi(l,u,x,u,e,i,r)||oi(x,u,x,_,e,i,r)||oi(x,_,l,_,e,i,r)||oi(l,_,l,u,e,i,r))return!0}else o+=hi(x,u,x,_,i,r),o+=hi(l,_,l,u,i,r);break;case uv.Z:if(n){if(oi(a,s,l,u,e,i,r))return!0}else o+=hi(a,s,l,u,i,r);a=l,s=u}}return n||ci(s,u)||(o+=hi(a,s,l,u,i,r)||0),0!==o}function vi(t,e,n){return mi(t,0,!1,e,n)}function yi(t,e,n,i){return mi(t,e,!0,n,i)}function xi(t){Xe.call(this,t),this.path=null}function _i(t,e,n,i,r,o,a,s,l,u,h){var c=l*(Cv/180),d=Iv(c)*(t-n)/2+Sv(c)*(e-i)/2,f=-1*Sv(c)*(t-n)/2+Iv(c)*(e-i)/2,p=d*d/(a*a)+f*f/(s*s);p>1&&(a*=Mv(p),s*=Mv(p));var g=(r===o?-1:1)*Mv((a*a*(s*s)-a*a*(f*f)-s*s*(d*d))/(a*a*(f*f)+s*s*(d*d)))||0,m=g*a*f/s,v=g*-s*d/a,y=(t+n)/2+Iv(c)*m-Sv(c)*v,x=(e+i)/2+Sv(c)*m+Iv(c)*v,_=Av([1,0],[(d-m)/a,(f-v)/s]),w=[(d-m)/a,(f-v)/s],b=[(-1*d-m)/a,(-1*f-v)/s],M=Av(w,b);Dv(w,b)<=-1&&(M=Cv),Dv(w,b)>=1&&(M=0),0===o&&M>0&&(M-=2*Cv),1===o&&M<0&&(M+=2*Cv),h.addData(u,y,x,a,s,_,M,c,o)}function wi(t){if(!t)return[];var e,n=t.replace(/-/g," -").replace(/ /g," ").replace(/ /g,",").replace(/,,/g,",");for(e=0;e0&&""===f[0]&&f.shift();for(var p=0;p=2){if(r&&"spline"!==r){var o=Rv(i,r,n,e.smoothConstraint);t.moveTo(i[0][0],i[0][1]);for(var a=i.length,s=0;s<(n?a:a-1);s++){var l=o[2*s],u=o[2*s+1],h=i[(s+1)%a];t.bezierCurveTo(l[0],l[1],u[0],u[1],h[0],h[1])}}else{"spline"===r&&(i=Nv(i,n)),t.moveTo(i[0][0],i[0][1]);for(var s=1,c=i.length;s=0)&&(i={textFill:null,textStroke:t.textStroke,textStrokeWidth:t.textStrokeWidth},t.textFill="#fff",null==t.textStroke&&(t.textStroke=n.autoColor,null==t.textStrokeWidth&&(t.textStrokeWidth=2))),i}function ir(t){var e=t.insideRollback;e&&(t.textFill=e.textFill,t.textStroke=e.textStroke,t.textStrokeWidth=e.textStrokeWidth)}function rr(t,e){var n=e||e.getModel("textStyle");return L([t.fontStyle||n&&n.getShallow("fontStyle")||"",t.fontWeight||n&&n.getShallow("fontWeight")||"",(t.fontSize||n&&n.getShallow("fontSize")||12)+"px",t.fontFamily||n&&n.getShallow("fontFamily")||"sans-serif"].join(" "))}function or(t,e,n,i,r,o){if("function"==typeof r&&(o=r,r=null),i&&i.isAnimationEnabled()){var a=t?"Update":"",s=i.getShallow("animationDuration"+a),l=i.getShallow("animationEasing"+a),u=i.getShallow("animationDelay"+a);"function"==typeof u&&(u=u(r,i.getAnimationDelayParams?i.getAnimationDelayParams(e,r):null)),"function"==typeof s&&(s=s(r)),s>0?e.animateTo(n,s,u||0,l,o,!!o):(e.stopAnimation(),e.attr(n),o&&o())}else e.stopAnimation(),e.attr(n),o&&o()}function ar(t,e,n,i,r){or(!0,t,e,n,i,r)}function sr(t,e,n,i,r){or(!1,t,e,n,i,r)}function lr(t,e){for(var n=ot([]);t&&t!==e;)st(n,t.getLocalTransform(),n),t=t.parent;return n}function ur(t,e,n){return e&&!c(e)&&(e=Qp.getLocalTransform(e)),n&&(e=ct([],e)),$([],t,e)}function hr(t,e,n){var i=0===e[4]||0===e[5]||0===e[0]?1:Math.abs(2*e[4]/e[0]),r=0===e[4]||0===e[5]||0===e[2]?1:Math.abs(2*e[4]/e[2]),o=["left"===t?-i:"right"===t?i:0,"top"===t?-r:"bottom"===t?r:0];return o=ur(o,e,n),Math.abs(o[0])>Math.abs(o[1])?o[0]>0?"right":"left":o[1]>0?"bottom":"top"}function cr(t,e,n,i){function r(t){var e={position:F(t.position),rotation:t.rotation};return t.shape&&(e.shape=o({},t.shape)),e}if(t&&e){var a=function(t){var e={};return t.traverse(function(t){!t.isGroup&&t.anid&&(e[t.anid]=t)}),e}(t);e.traverse(function(t){if(!t.isGroup&&t.anid){var e=a[t.anid];if(e){var i=r(t);t.attr(r(e)),ar(t,i,n,t.dataIndex)}}})}}function dr(t,e){return f(t,function(t){var n=t[0];n=Kv(n,e.x),n=Qv(n,e.x+e.width);var i=t[1];return i=Kv(i,e.y),i=Qv(i,e.y+e.height),[n,i]})}function fr(t,e,n){var i=(e=o({rectHover:!0},e)).style={strokeNoScale:!0};if(n=n||{x:-1,y:-1,width:2,height:2},t)return 0===t.indexOf("image://")?(i.image=t.slice(8),a(i,n),new je(e)):ki(t.replace("path://",""),e,n,"center")}function pr(t,e,n){this.parentModel=e,this.ecModel=n,this.option=t}function gr(t,e,n){for(var i=0;i0){if(t<=e[0])return n[0];if(t>=e[1])return n[1]}else{if(t>=e[0])return n[0];if(t<=e[1])return n[1]}else{if(t===e[0])return n[0];if(t===e[1])return n[1]}return(t-e[0])/r*o+n[0]}function _r(t,e){switch(t){case"center":case"middle":t="50%";break;case"left":case"top":t="0%";break;case"right":case"bottom":t="100%"}return"string"==typeof t?yr(t).match(/%$/)?parseFloat(t)/100*e:parseFloat(t):null==t?NaN:+t}function wr(t,e,n){return null==e&&(e=10),e=Math.min(Math.max(0,e),20),t=(+t).toFixed(e),n?t:+t}function br(t){return t.sort(function(t,e){return t-e}),t}function Mr(t){if(t=+t,isNaN(t))return 0;for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}function Sr(t){var e=t.toString(),n=e.indexOf("e");if(n>0){var i=+e.slice(n+1);return i<0?-i:0}var r=e.indexOf(".");return r<0?0:e.length-1-r}function Ir(t,e){var n=Math.log,i=Math.LN10,r=Math.floor(n(t[1]-t[0])/i),o=Math.round(n(Math.abs(e[1]-e[0]))/i),a=Math.min(Math.max(-r+o,0),20);return isFinite(a)?a:20}function Cr(t,e,n){if(!t[e])return 0;var i=p(t,function(t,e){return t+(isNaN(e)?0:e)},0);if(0===i)return 0;for(var r=Math.pow(10,n),o=f(t,function(t){return(isNaN(t)?0:t)/i*r*100}),a=100*r,s=f(o,function(t){return Math.floor(t)}),l=p(s,function(t,e){return t+e},0),u=f(o,function(t,e){return t-s[e]});lh&&(h=u[d],c=d);++s[c],u[c]=0,++l}return s[e]/r}function Tr(t){var e=2*Math.PI;return(t%e+e)%e}function Dr(t){return t>-ly&&t=-20?+t.toFixed(i<0?-i:0):t}function Or(t){return isNaN(t)?"-":(t=(t+"").split("."))[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,"$1,")+(t.length>1?"."+t[1]:"")}function zr(t,e){return t=(t||"").toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()}),e&&t&&(t=t.charAt(0).toUpperCase()+t.slice(1)),t}function Er(t){return null==t?"":(t+"").replace(dy,function(t,e){return fy[e]})}function Nr(t,e,n){y(e)||(e=[e]);var i=e.length;if(!i)return"";for(var r=e[0].$vars||[],o=0;o':'':""}function Br(t,e){return t+="","0000".substr(0,e-t.length)+t}function Vr(t,e,n){"week"!==t&&"month"!==t&&"quarter"!==t&&"half-year"!==t&&"year"!==t||(t="MM-dd\nyyyy");var i=Ar(e),r=n?"UTC":"",o=i["get"+r+"FullYear"](),a=i["get"+r+"Month"]()+1,s=i["get"+r+"Date"](),l=i["get"+r+"Hours"](),u=i["get"+r+"Minutes"](),h=i["get"+r+"Seconds"](),c=i["get"+r+"Milliseconds"]();return t=t.replace("MM",Br(a,2)).replace("M",a).replace("yyyy",o).replace("yy",o%100).replace("dd",Br(s,2)).replace("d",s).replace("hh",Br(l,2)).replace("h",l).replace("mm",Br(u,2)).replace("m",u).replace("ss",Br(h,2)).replace("s",h).replace("SSS",Br(c,3))}function Fr(t){return t?t.charAt(0).toUpperCase()+t.substr(1):t}function Hr(t,e,n,i,r){var o=0,a=0;null==i&&(i=1/0),null==r&&(r=1/0);var s=0;e.eachChild(function(l,u){var h,c,d=l.position,f=l.getBoundingRect(),p=e.childAt(u+1),g=p&&p.getBoundingRect();if("horizontal"===t){var m=f.width+(g?-g.x+f.x:0);(h=o+m)>i||l.newline?(o=0,h=m,a+=s+n,s=f.height):s=Math.max(s,f.height)}else{var v=f.height+(g?-g.y+f.y:0);(c=a+v)>r||l.newline?(o+=s+n,a=0,c=v,s=f.width):s=Math.max(s,f.width)}l.newline||(d[0]=o,d[1]=a,"horizontal"===t?o=h+n:a=c+n)})}function Gr(t,e,n){n=cy(n||0);var i=e.width,r=e.height,o=_r(t.left,i),a=_r(t.top,r),s=_r(t.right,i),l=_r(t.bottom,r),u=_r(t.width,i),h=_r(t.height,r),c=n[2]+n[0],d=n[1]+n[3],f=t.aspect;switch(isNaN(u)&&(u=i-s-d-o),isNaN(h)&&(h=r-l-c-a),null!=f&&(isNaN(u)&&isNaN(h)&&(f>i/r?u=.8*i:h=.8*r),isNaN(u)&&(u=f*h),isNaN(h)&&(h=u/f)),isNaN(o)&&(o=i-s-u-d),isNaN(a)&&(a=r-l-h-c),t.left||t.right){case"center":o=i/2-u/2-n[3];break;case"right":o=i-u-d}switch(t.top||t.bottom){case"middle":case"center":a=r/2-h/2-n[0];break;case"bottom":a=r-h-c}o=o||0,a=a||0,isNaN(u)&&(u=i-d-o-(s||0)),isNaN(h)&&(h=r-c-a-(l||0));var p=new Xt(o+n[3],a+n[0],u,h);return p.margin=n,p}function Wr(t,e,n,i,r){var o=!r||!r.hv||r.hv[0],s=!r||!r.hv||r.hv[1],l=r&&r.boundingMode||"all";if(o||s){var u;if("raw"===l)u="group"===t.type?new Xt(0,0,+e.width||0,+e.height||0):t.getBoundingRect();else if(u=t.getBoundingRect(),t.needLocalTransform()){var h=t.getLocalTransform();(u=u.clone()).applyTransform(h)}e=Gr(a({width:u.width,height:u.height},e),n,i);var c=t.position,d=o?e.x-u.x:0,f=s?e.y-u.y:0;t.attr("position","raw"===l?[d,f]:[c[0]+d,c[1]+f])}}function Zr(t,e,n){function i(n,i){var a={},l=0,u={},h=0;if(xy(n,function(e){u[e]=t[e]}),xy(n,function(t){r(e,t)&&(a[t]=u[t]=e[t]),o(a,t)&&l++,o(u,t)&&h++}),s[i])return o(e,n[1])?u[n[2]]=null:o(e,n[2])&&(u[n[1]]=null),u;if(2!==h&&l){if(l>=2)return a;for(var c=0;ce)return t[i];return t[n-1]}function Yr(t){var e=t.get("coordinateSystem"),n={coordSysName:e,coordSysDims:[],axisMap:N(),categoryAxisMap:N()},i=ky[e];if(i)return i(t,n,n.axisMap,n.categoryAxisMap),n}function qr(t){return"category"===t.get("type")}function $r(t){this.fromDataset=t.fromDataset,this.data=t.data||(t.sourceFormat===zy?{}:[]),this.sourceFormat=t.sourceFormat||Ey,this.seriesLayoutBy=t.seriesLayoutBy||Ry,this.dimensionsDefine=t.dimensionsDefine,this.encodeDefine=t.encodeDefine&&N(t.encodeDefine),this.startIndex=t.startIndex||0,this.dimensionsDetectCount=t.dimensionsDetectCount}function Kr(t){var e=t.option.source,n=Ey;if(M(e))n=Ny;else if(y(e))for(var i=0,r=e.length;i=e:"max"===n?t<=e:t===e}function Mo(t,e){return t.join(",")===e.join(",")}function So(t,e){Zy(e=e||{},function(e,n){if(null!=e){var i=t[n];if(Iy.hasClass(n)){e=xn(e);var r=Mn(i=xn(i),e);t[n]=Xy(r,function(t){return t.option&&t.exist?jy(t.exist,t.option,!0):t.exist||t.option})}else t[n]=jy(i,e,!0)}})}function Io(t){var e=t&&t.itemStyle;if(e)for(var n=0,r=Ky.length;n=0;p--){var g=t[p];if(s||(d=g.data.rawIndexOf(g.stackedByDimension,c)),d>=0){var m=g.data.getByRawIndex(g.stackResultDimension,d);if(h>=0&&m>0||h<=0&&m<0){h+=m,f=m;break}}}return i[0]=h,i[1]=f,i});a.hostModel.setData(l),e.data=l})}function Ro(t,e){$r.isInstance(t)||(t=$r.seriesDataToSource(t)),this._source=t;var n=this._data=t.data,i=t.sourceFormat;i===Ny&&(this._offset=0,this._dimSize=e,this._data=n),o(this,ix[i===Ly?i+"_"+t.seriesLayoutBy:i])}function Bo(){return this._data.length}function Vo(t){return this._data[t]}function Fo(t){for(var e=0;ee.outputData.count()&&e.model.getRawData().cloneShallow(e.outputData)}function ea(t,e){d(t.CHANGABLE_METHODS,function(n){t.wrapMethod(n,v(na,e))})}function na(t){var e=ia(t);e&&e.setOutputEnd(this.count())}function ia(t){var e=(t.ecModel||{}).scheduler,n=e&&e.getPipeline(t.uid);if(n){var i=n.currentTask;if(i){var r=i.agentStubMap;r&&(i=r.get(t.uid))}return i}}function ra(){this.group=new Sg,this.uid=vr("viewChart"),this.renderTask=Xo({plan:sa,reset:la}),this.renderTask.context={view:this}}function oa(t,e){if(t&&(t.trigger(e),"group"===t.type))for(var n=0;n=0?i():c=setTimeout(i,-o),u=r};return d.clear=function(){c&&(clearTimeout(c),c=null)},d.debounceNextCall=function(t){l=t},d}function ha(t,e,n,i){var r=t[e];if(r){var o=r[xx]||r,a=r[bx];if(r[_x]!==n||a!==i){if(null==n||!i)return t[e]=o;(r=t[e]=ua(o,n,"debounce"===i))[xx]=o,r[bx]=i,r[_x]=n}return r}}function ca(t,e){var n=t[e];n&&n[xx]&&(t[e]=n[xx])}function da(t,e,n,i){this.ecInstance=t,this.api=e,this.unfinished;var n=this._dataProcessorHandlers=n.slice(),i=this._visualHandlers=i.slice();this._allHandlers=n.concat(i),this._stageTaskMap=N()}function fa(t,e,n,i,r){function o(t,e){return t.setDirty&&(!t.dirtyMap||t.dirtyMap.get(e.__pipeline.id))}r=r||{};var a;d(e,function(e,s){if(!r.visualType||r.visualType===e.visualType){var l=t._stageTaskMap.get(e.uid),u=l.seriesTaskMap,h=l.overallTask;if(h){var c,d=h.agentStubMap;d.each(function(t){o(r,t)&&(t.dirty(),c=!0)}),c&&h.dirty(),Dx(h,i);var f=t.getPerformArgs(h,r.block);d.each(function(t){t.perform(f)}),a|=h.perform(f)}else u&&u.each(function(s,l){o(r,s)&&s.dirty();var u=t.getPerformArgs(s,r.block);u.skip=!e.performRawSeries&&n.isSeriesFiltered(s.context.model),Dx(s,i),a|=s.perform(u)})}}),t.unfinished|=a}function pa(t,e,n,i,r){function o(n){var o=n.uid,s=a.get(o)||a.set(o,Xo({plan:_a,reset:wa,count:Ma}));s.context={model:n,ecModel:i,api:r,useClearVisual:e.isVisual&&!e.isLayout,plan:e.plan,reset:e.reset,scheduler:t},Sa(t,n,s)}var a=n.seriesTaskMap||(n.seriesTaskMap=N()),s=e.seriesType,l=e.getTargetSeries;e.createOnAllSeries?i.eachRawSeries(o):s?i.eachRawSeriesByType(s,o):l&&l(i,r).each(o);var u=t._pipelineMap;a.each(function(t,e){u.get(e)||(t.dispose(),a.removeKey(e))})}function ga(t,e,n,i,r){function o(e){var n=e.uid,i=s.get(n);i||(i=s.set(n,Xo({reset:va,onDirty:xa})),a.dirty()),i.context={model:e,overallProgress:h,modifyOutputEnd:c},i.agent=a,i.__block=h,Sa(t,e,i)}var a=n.overallTask=n.overallTask||Xo({reset:ma});a.context={ecModel:i,api:r,overallReset:e.overallReset,scheduler:t};var s=a.agentStubMap=a.agentStubMap||N(),l=e.seriesType,u=e.getTargetSeries,h=!0,c=e.modifyOutputEnd;l?i.eachRawSeriesByType(l,o):u?u(i,r).each(o):(h=!1,d(i.getSeries(),o));var f=t._pipelineMap;s.each(function(t,e){f.get(e)||(t.dispose(),a.dirty(),s.removeKey(e))})}function ma(t){t.overallReset(t.ecModel,t.api,t.payload)}function va(t,e){return t.overallProgress&&ya}function ya(){this.agent.dirty(),this.getDownstream().dirty()}function xa(){this.agent&&this.agent.dirty()}function _a(t){return t.plan&&t.plan(t.model,t.ecModel,t.api,t.payload)}function wa(t){t.useClearVisual&&t.data.clearAllVisual();var e=t.resetDefines=xn(t.reset(t.model,t.ecModel,t.api,t.payload));return e.length>1?f(e,function(t,e){return ba(e)}):Ax}function ba(t){return function(e,n){var i=n.data,r=n.resetDefines[t];if(r&&r.dataEach)for(var o=e.start;oe.get("hoverLayerThreshold")&&!bp.node&&n.traverse(function(t){t.isGroup||(t.useHoverLayer=!0)})}function Ua(t,e){var n=t.get("blendMode")||null;e.group.traverse(function(t){t.isGroup||t.style.blend!==n&&t.setStyle("blend",n),t.eachPendingDisplayable&&t.eachPendingDisplayable(function(t){t.setStyle("blend",n)})})}function Xa(t,e){var n=t.get("z"),i=t.get("zlevel");e.group.traverse(function(t){"group"!==t.type&&(null!=n&&(t.z=n),null!=i&&(t.zlevel=i))})}function ja(t){var e=t._coordSysMgr;return o(new vo(t),{getCoordinateSystems:m(e.getCoordinateSystems,e),getComponentByElement:function(e){for(;e;){var n=e.__ecComponentInfo;if(null!=n)return t._model.getComponent(n.mainType,n.index);e=e.parent}}})}function Ya(t){function e(t,e){for(var i=0;i65535?x_:__}function ms(t){var e=t.constructor;return e===Array?t.slice():new e(t)}function vs(t,e){d(w_.concat(e.__wrappedMethods||[]),function(n){e.hasOwnProperty(n)&&(t[n]=e[n])}),t.__wrappedMethods=e.__wrappedMethods,d(b_,function(i){t[i]=n(e[i])}),t._calculationInfo=o(e._calculationInfo)}function ys(t){var e=t._invertedIndicesMap;d(e,function(n,i){var r=t._dimensionInfos[i].ordinalMeta;if(r){n=e[i]=new x_(r.categories.length);for(o=0;o=0?this._indices[t]:-1}function bs(t,e){var n=t._idList[e];return null==n&&(n=xs(t,t._idDimIdx,e)),null==n&&(n=v_+e),n}function Ms(t){return y(t)||(t=[t]),t}function Ss(t,e){var n=t.dimensions,i=new M_(f(n,t.getDimensionInfo,t),t.hostModel);vs(i,t);for(var r=i._storage={},o=t._storage,a=0;a=0?(r[s]=Is(o[s]),i._rawExtent[s]=Cs(),i._extent[s]=null):r[s]=o[s])}return i}function Is(t){for(var e=new Array(t.length),n=0;ni&&(a=r.interval=i);var s=r.intervalPrecision=Hs(a);return Ws(r.niceTickExtent=[k_(Math.ceil(t[0]/a)*a,s),k_(Math.floor(t[1]/a)*a,s)],t),r}function Hs(t){return Sr(t)+2}function Gs(t,e,n){t[e]=Math.max(Math.min(t[e],n[1]),n[0])}function Ws(t,e){!isFinite(t[0])&&(t[0]=e[0]),!isFinite(t[1])&&(t[1]=e[1]),Gs(t,0,e),Gs(t,1,e),t[0]>t[1]&&(t[0]=t[1])}function Zs(t,e,n,i){var r=[];if(!t)return r;e[0]1e4)return[];return e[1]>(r.length?r[r.length-1]:n[1])&&r.push(e[1]),r}function Us(t){return t.get("stack")||O_+t.seriesIndex}function Xs(t){return t.dim+t.index}function js(t,e){var n=[];return e.eachSeriesByType(t,function(t){Ks(t)&&!Qs(t)&&n.push(t)}),n}function Ys(t){var e=[];return d(t,function(t){var n=t.getData(),i=t.coordinateSystem.getBaseAxis(),r=i.getExtent(),o="category"===i.type?i.getBandWidth():Math.abs(r[1]-r[0])/n.count(),a=_r(t.get("barWidth"),o),s=_r(t.get("barMaxWidth"),o),l=t.get("barGap"),u=t.get("barCategoryGap");e.push({bandWidth:o,barWidth:a,barMaxWidth:s,barGap:l,barCategoryGap:u,axisKey:Xs(i),stackId:Us(t)})}),qs(e)}function qs(t){var e={};d(t,function(t,n){var i=t.axisKey,r=t.bandWidth,o=e[i]||{bandWidth:r,remainedWidth:r,autoWidthCount:0,categoryGap:"20%",gap:"30%",stacks:{}},a=o.stacks;e[i]=o;var s=t.stackId;a[s]||o.autoWidthCount++,a[s]=a[s]||{width:0,maxWidth:0};var l=t.barWidth;l&&!a[s].width&&(a[s].width=l,l=Math.min(o.remainedWidth,l),o.remainedWidth-=l);var u=t.barMaxWidth;u&&(a[s].maxWidth=u);var h=t.barGap;null!=h&&(o.gap=h);var c=t.barCategoryGap;null!=c&&(o.categoryGap=c)});var n={};return d(e,function(t,e){n[e]={};var i=t.stacks,r=t.bandWidth,o=_r(t.categoryGap,r),a=_r(t.gap,1),s=t.remainedWidth,l=t.autoWidthCount,u=(s-o)/(l+(l-1)*a);u=Math.max(u,0),d(i,function(t,e){var n=t.maxWidth;n&&n=0||n?e.toGlobalCoord(e.dataToCoord(0)):e.getGlobalExtent()[0]}function tl(t,e){return U_(t,Z_(e))}function el(t,e){var n,i,r,o=t.type,a=e.getMin(),s=e.getMax(),l=null!=a,u=null!=s,h=t.getExtent();"ordinal"===o?n=e.getCategories().length:(y(i=e.get("boundaryGap"))||(i=[i||0,i||0]),"boolean"==typeof i[0]&&(i=[0,0]),i[0]=_r(i[0],1),i[1]=_r(i[1],1),r=h[1]-h[0]||Math.abs(h[0])),null==a&&(a="ordinal"===o?n?0:NaN:h[0]-i[0]*r),null==s&&(s="ordinal"===o?n?n-1:NaN:h[1]+i[1]*r),"dataMin"===a?a=h[0]:"function"==typeof a&&(a=a({min:h[0],max:h[1]})),"dataMax"===s?s=h[1]:"function"==typeof s&&(s=s({min:h[0],max:h[1]})),(null==a||!isFinite(a))&&(a=NaN),(null==s||!isFinite(s))&&(s=NaN),t.setBlank(I(a)||I(s)||"ordinal"===o&&!t.getOrdinalMeta().categories.length),e.getNeedCrossZero()&&(a>0&&s>0&&!l&&(a=0),a<0&&s<0&&!u&&(s=0));var c=e.ecModel;if(c&&"time"===o){var f,p=js("bar",c);if(d(p,function(t){f|=t.getBaseAxis()===e.axis}),f){var g=Ys(p),m=nl(a,s,e,g);a=m.min,s=m.max}}return[a,s]}function nl(t,e,n,i){var r=n.axis.getExtent(),o=r[1]-r[0],a=$s(i,n.axis);if(void 0===a)return{min:t,max:e};var s=1/0;d(a,function(t){s=Math.min(t.offset,s)});var l=-1/0;d(a,function(t){l=Math.max(t.offset+t.width,l)}),s=Math.abs(s),l=Math.abs(l);var u=s+l,h=e-t,c=h/(1-(s+l)/o)-h;return e+=c*(l/u),t-=c*(s/u),{min:t,max:e}}function il(t,e){var n=el(t,e),i=null!=e.getMin(),r=null!=e.getMax(),o=e.get("splitNumber");"log"===t.type&&(t.base=e.get("logBase"));var a=t.type;t.setExtent(n[0],n[1]),t.niceExtent({splitNumber:o,fixMin:i,fixMax:r,minInterval:"interval"===a||"time"===a?e.get("minInterval"):null,maxInterval:"interval"===a||"time"===a?e.get("maxInterval"):null});var s=e.get("interval");null!=s&&t.setInterval&&t.setInterval(s)}function rl(t,e){if(e=e||t.get("type"))switch(e){case"category":return new A_(t.getOrdinalMeta?t.getOrdinalMeta():t.getCategories(),[1/0,-1/0]);case"value":return new L_;default:return(Ns.getClass(e)||L_).create(t)}}function ol(t){var e=t.scale.getExtent(),n=e[0],i=e[1];return!(n>0&&i>0||n<0&&i<0)}function al(t){var e=t.getLabelModel().get("formatter"),n="category"===t.type?t.scale.getExtent()[0]:null;return"string"==typeof e?e=function(t){return function(e){return t.replace("{value}",null!=e?e:"")}}(e):"function"==typeof e?function(i,r){return null!=n&&(r=i-n),e(sl(t,i),r)}:function(e){return t.scale.getLabel(e)}}function sl(t,e){return"category"===t.type?t.scale.getLabel(e):e}function ll(t){var e=t.model,n=t.scale;if(e.get("axisLabel.show")&&!n.isBlank()){var i,r,o="category"===t.type,a=n.getExtent();r=o?n.count():(i=n.getTicks()).length;var s,l=t.getLabelModel(),u=al(t),h=1;r>40&&(h=Math.ceil(r/40));for(var c=0;c>1^-(1&s),l=l>>1^-(1&l),r=s+=r,o=l+=o,i.push([s/n,l/n])}return i}function vl(t){return"category"===t.type?xl(t):bl(t)}function yl(t,e){return"category"===t.type?wl(t,e):{ticks:t.scale.getTicks()}}function xl(t){var e=t.getLabelModel(),n=_l(t,e);return!e.get("show")||t.scale.isBlank()?{labels:[],labelCategoryInterval:n.labelCategoryInterval}:n}function _l(t,e){var n=Ml(t,"labels"),i=Pl(e),r=Sl(n,i);if(r)return r;var o,a;return o=x(i)?kl(t,i):Al(t,a="auto"===i?Cl(t):i),Il(n,i,{labels:o,labelCategoryInterval:a})}function wl(t,e){var n=Ml(t,"ticks"),i=Pl(e),r=Sl(n,i);if(r)return r;var o,a;if(e.get("show")&&!t.scale.isBlank()||(o=[]),x(i))o=kl(t,i,!0);else if("auto"===i){var s=_l(t,t.getLabelModel());a=s.labelCategoryInterval,o=f(s.labels,function(t){return t.tickValue})}else o=Al(t,a=i,!0);return Il(n,i,{ticks:o,tickCategoryInterval:a})}function bl(t){var e=t.scale.getTicks(),n=al(t);return{labels:f(e,function(e,i){return{formattedLabel:n(e,i),rawLabel:t.scale.getLabel(e),tickValue:e}})}}function Ml(t,e){return uw(t)[e]||(uw(t)[e]=[])}function Sl(t,e){for(var n=0;n40&&(s=Math.max(1,Math.floor(a/40)));for(var l=o[0],u=t.dataToCoord(l+1)-t.dataToCoord(l),h=Math.abs(u*Math.cos(i)),c=Math.abs(u*Math.sin(i)),d=0,f=0;l<=o[1];l+=s){var p=0,g=0,m=ce(n(l),e.font,"center","top");p=1.3*m.width,g=1.3*m.height,d=Math.max(d,p,7),f=Math.max(f,g,7)}var v=d/h,y=f/c;isNaN(v)&&(v=1/0),isNaN(y)&&(y=1/0);var x=Math.max(0,Math.floor(Math.min(v,y))),_=uw(t.model),w=_.lastAutoInterval,b=_.lastTickCount;return null!=w&&null!=b&&Math.abs(w-x)<=1&&Math.abs(b-a)<=1&&w>x?x=w:(_.lastTickCount=a,_.lastAutoInterval=x),x}function Dl(t){var e=t.getLabelModel();return{axisRotate:t.getRotate?t.getRotate():t.isHorizontal&&!t.isHorizontal()?90:0,labelRotate:e.get("rotate")||0,font:e.getFont()}}function Al(t,e,n){function i(t){l.push(n?t:{formattedLabel:r(t),rawLabel:o.getLabel(t),tickValue:t})}var r=al(t),o=t.scale,a=o.getExtent(),s=t.getLabelModel(),l=[],u=Math.max((e||0)+1,1),h=a[0],c=o.count();0!==h&&u>1&&c/u>2&&(h=Math.round(Math.ceil(h/u)*u));var d={min:s.get("showMinLabel"),max:s.get("showMaxLabel")};d.min&&h!==a[0]&&i(a[0]);for(var f=h;f<=a[1];f+=u)i(f);return d.max&&f!==a[1]&&i(a[1]),l}function kl(t,e,n){var i=t.scale,r=al(t),o=[];return d(i.getTicks(),function(t){var a=i.getLabel(t);e(t,a)&&o.push(n?t:{formattedLabel:r(t),rawLabel:a,tickValue:t})}),o}function Pl(t){var e=t.get("interval");return null==e?"auto":e}function Ll(t,e){var n=(t[1]-t[0])/e/2;t[0]+=n,t[1]-=n}function Ol(t,e,n,i,r){function o(t,e){return h?t>e:t0&&(t.coord-=u/(2*(e+1)))}),s={coord:e[a-1].coord+u},e.push(s)}var h=l[0]>l[1];o(e[0].coord,l[0])&&(r?e[0].coord=l[0]:e.shift()),r&&o(l[0],e[0].coord)&&e.unshift({coord:l[0]}),o(l[1],s.coord)&&(r?s.coord=l[1]:e.pop()),r&&o(s.coord,l[1])&&e.push({coord:l[1]})}}function zl(t,e){var n=t.mapDimension("defaultedLabel",!0),i=n.length;if(1===i)return Zo(t,e,n[0]);if(i){for(var r=[],o=0;o0?n=i[0]:i[1]<0&&(n=i[1]),n}function Zl(t,e,n,i){var r=NaN;t.stacked&&(r=n.get(n.getCalculationInfo("stackedOverDimension"),i)),isNaN(r)&&(r=t.valueStart);var o=t.baseDataOffset,a=[];return a[o]=n.get(t.baseDim,i),a[1-o]=r,e.dataToPoint(a)}function Ul(t,e){var n=[];return e.diff(t).add(function(t){n.push({cmd:"+",idx:t})}).update(function(t,e){n.push({cmd:"=",idx:e,idx1:t})}).remove(function(t){n.push({cmd:"-",idx:t})}).execute(),n}function Xl(t){return isNaN(t[0])||isNaN(t[1])}function jl(t,e,n,i,r,o,a,s,l,u,h){return"none"!==u&&u?Yl.apply(this,arguments):ql.apply(this,arguments)}function Yl(t,e,n,i,r,o,a,s,l,u,h){for(var c=0,d=n,f=0;f=r||d<0)break;if(Xl(p)){if(h){d+=o;continue}break}if(d===n)t[o>0?"moveTo":"lineTo"](p[0],p[1]);else if(l>0){var g=e[c],m="y"===u?1:0,v=(p[m]-g[m])*l;Iw(Tw,g),Tw[m]=g[m]+v,Iw(Dw,p),Dw[m]=p[m]-v,t.bezierCurveTo(Tw[0],Tw[1],Dw[0],Dw[1],p[0],p[1])}else t.lineTo(p[0],p[1]);c=d,d+=o}return f}function ql(t,e,n,i,r,o,a,s,l,u,h){for(var c=0,d=n,f=0;f=r||d<0)break;if(Xl(p)){if(h){d+=o;continue}break}if(d===n)t[o>0?"moveTo":"lineTo"](p[0],p[1]),Iw(Tw,p);else if(l>0){var g=d+o,m=e[g];if(h)for(;m&&Xl(e[g]);)m=e[g+=o];var v=.5,y=e[c];if(!(m=e[g])||Xl(m))Iw(Dw,p);else{Xl(m)&&!h&&(m=p),W(Cw,m,y);var x,_;if("x"===u||"y"===u){var w="x"===u?0:1;x=Math.abs(p[w]-y[w]),_=Math.abs(p[w]-m[w])}else x=Fp(p,y),_=Fp(p,m);Sw(Dw,p,Cw,-l*(1-(v=_/(_+x))))}bw(Tw,Tw,s),Mw(Tw,Tw,a),bw(Dw,Dw,s),Mw(Dw,Dw,a),t.bezierCurveTo(Tw[0],Tw[1],Dw[0],Dw[1],p[0],p[1]),Sw(Tw,p,Cw,l*v)}else t.lineTo(p[0],p[1]);c=d,d+=o}return f}function $l(t,e){var n=[1/0,1/0],i=[-1/0,-1/0];if(e)for(var r=0;ri[0]&&(i[0]=o[0]),o[1]>i[1]&&(i[1]=o[1])}return{min:e?n:i,max:e?i:n}}function Kl(t,e){if(t.length===e.length){for(var n=0;ne[0]?1:-1;e[0]+=i*n,e[1]-=i*n}return e}function tu(t,e,n){if(!n.valueDim)return[];for(var i=[],r=0,o=e.count();ro[1]&&o.reverse();var a=r.getExtent(),s=Math.PI/180;n&&(o[0]-=.5,o[1]+=.5);var l=new zv({shape:{cx:wr(t.cx,1),cy:wr(t.cy,1),r0:wr(o[0],1),r:wr(o[1],1),startAngle:-a[0]*s,endAngle:-a[1]*s,clockwise:r.inverse}});return e&&(l.shape.endAngle=-a[0]*s,sr(l,{shape:{endAngle:-a[1]*s}},i)),l}function iu(t,e,n,i){return"polar"===t.type?nu(t,e,n,i):eu(t,e,n,i)}function ru(t,e,n){for(var i=e.getBaseAxis(),r="x"===i.dim||"radius"===i.dim?0:1,o=[],a=0;a=0;o--){var a=n[o].dimension,s=t.dimensions[a],l=t.getDimensionInfo(s);if("x"===(i=l&&l.coordDim)||"y"===i){r=n[o];break}}if(r){var u=e.getAxis(i),h=f(r.stops,function(t){return{coord:u.toGlobalCoord(u.dataToCoord(t.value)),color:t.color}}),c=h.length,p=r.outerColors.slice();c&&h[0].coord>h[c-1].coord&&(h.reverse(),p.reverse());var g=h[0].coord-10,m=h[c-1].coord+10,v=m-g;if(v<.001)return"transparent";d(h,function(t){t.offset=(t.coord-g)/v}),h.push({offset:c?h[c-1].offset:.5,color:p[1]||"transparent"}),h.unshift({offset:c?h[0].offset:.5,color:p[0]||"transparent"});var y=new jv(0,0,0,0,h,!0);return y[i]=g,y[i+"2"]=m,y}}}function au(t,e,n){var i=t.get("showAllSymbol"),r="auto"===i;if(!i||r){var o=n.getAxesByScale("ordinal")[0];if(o&&(!r||!su(o,e))){var a=e.mapDimension(o.dim),s={};return d(o.getViewLabels(),function(t){s[t.tickValue]=1}),function(t){return!s.hasOwnProperty(e.get(a,t))}}}}function su(t,e){var n=t.getExtent(),i=Math.abs(n[1]-n[0])/t.scale.count();isNaN(i)&&(i=0);for(var r=e.count(),o=Math.max(1,Math.round(r/5)),a=0;ai)return!1;return!0}function lu(t){return this._axes[t]}function uu(t){Ew.call(this,t)}function hu(t,e){return e.type||(e.data?"category":"value")}function cu(t,e,n){return t.getCoordSysModel()===e}function du(t,e,n){this._coordsMap={},this._coordsList=[],this._axesMap={},this._axesList=[],this._initCartesian(t,e,n),this.model=t}function fu(t,e,n){n.getAxesOnZeroOf=function(){return i?[i]:[]};var i,r=t[e],o=n.model,a=o.get("axisLine.onZero"),s=o.get("axisLine.onZeroAxisIndex");if(a)if(null==s){for(var l in r)if(r.hasOwnProperty(l)&&pu(r[l])){i=r[l];break}}else pu(r[s])&&(i=r[s])}function pu(t){return t&&"category"!==t.type&&"time"!==t.type&&ol(t)}function gu(t,e){var n=t.getExtent(),i=n[0]+n[1];t.toGlobalCoord="x"===t.dim?function(t){return t+e}:function(t){return i-t+e},t.toLocalCoord="x"===t.dim?function(t){return t-e}:function(t){return i-t+e}}function mu(t,e){return f(Zw,function(e){return t.getReferringComponents(e)[0]})}function vu(t){return"cartesian2d"===t.get("coordinateSystem")}function yu(t){var e={componentType:t.mainType};return e[t.mainType+"Index"]=t.componentIndex,e}function xu(t,e,n,i){var r,o,a=Tr(n-t.rotation),s=i[0]>i[1],l="start"===e&&!s||"start"!==e&&s;return Dr(a-Uw/2)?(o=l?"bottom":"top",r="center"):Dr(a-1.5*Uw)?(o=l?"top":"bottom",r="center"):(o="middle",r=a<1.5*Uw&&a>Uw/2?l?"left":"right":l?"right":"left"),{rotation:a,textAlign:r,textVerticalAlign:o}}function _u(t){var e=t.get("tooltip");return t.get("silent")||!(t.get("triggerEvent")||e&&e.show)}function wu(t,e,n){var i=t.get("axisLabel.showMinLabel"),r=t.get("axisLabel.showMaxLabel");e=e||[],n=n||[];var o=e[0],a=e[1],s=e[e.length-1],l=e[e.length-2],u=n[0],h=n[1],c=n[n.length-1],d=n[n.length-2];!1===i?(bu(o),bu(u)):Mu(o,a)&&(i?(bu(a),bu(h)):(bu(o),bu(u))),!1===r?(bu(s),bu(c)):Mu(l,s)&&(r?(bu(l),bu(d)):(bu(s),bu(c)))}function bu(t){t&&(t.ignore=!0)}function Mu(t,e,n){var i=t&&t.getBoundingRect().clone(),r=e&&e.getBoundingRect().clone();if(i&&r){var o=ot([]);return ut(o,o,-t.rotation),i.applyTransform(st([],o,t.getLocalTransform())),r.applyTransform(st([],o,e.getLocalTransform())),i.intersect(r)}}function Su(t){return"middle"===t||"center"===t}function Iu(t,e,n){var i=e.axis;if(e.get("axisTick.show")&&!i.scale.isBlank()){for(var r=e.getModel("axisTick"),o=r.getModel("lineStyle"),s=r.get("length"),l=i.getTicksCoords(),u=[],h=[],c=t._transform,d=[],f=0;f=0||t===e}function Ou(t){var e=zu(t);if(e){var n=e.axisPointerModel,i=e.axis.scale,r=n.option,o=n.get("status"),a=n.get("value");null!=a&&(a=i.parse(a));var s=Nu(n);null==o&&(r.status=s?"show":"hide");var l=i.getExtent().slice();l[0]>l[1]&&l.reverse(),(null==a||a>l[1])&&(a=l[1]),a0?"bottom":"top":r.width>0?"left":"right";l||Hu(t.style,d,i,u,o,n,p),qi(t,d)}function Xu(t,e){var n=t.get(rb)||0;return Math.min(n,Math.abs(e.width),Math.abs(e.height))}function ju(t,e,n){var i=t.getData(),r=[],o=i.getLayout("valueAxisHorizontal")?1:0;r[1-o]=i.getLayout("valueAxisStart");var a=new sb({shape:{points:i.getLayout("largePoints")},incremental:!!n,__startPoint:r,__valueIdx:o});e.add(a),Yu(a,t,i)}function Yu(t,e,n){var i=n.getVisual("borderColor")||n.getVisual("color"),r=e.getModel("itemStyle").getItemStyle(["color","borderColor"]);t.useStyle(r),t.style.fill=null,t.style.stroke=i,t.style.lineWidth=n.getLayout("barWidth")}function qu(t,e,n,i){var r=e.getData(),o=this.dataIndex,a=r.getName(o),s=e.get("selectedOffset");i.dispatchAction({type:"pieToggleSelect",from:t,name:a,seriesId:e.id}),r.each(function(t){$u(r.getItemGraphicEl(t),r.getItemLayout(t),e.isSelected(r.getName(t)),s,n)})}function $u(t,e,n,i,r){var o=(e.startAngle+e.endAngle)/2,a=Math.cos(o),s=Math.sin(o),l=n?i:0,u=[a*l,s*l];r?t.animate().when(200,{position:u}).start("bounceOut"):t.attr("position",u)}function Ku(t,e){function n(){o.ignore=o.hoverIgnore,a.ignore=a.hoverIgnore}function i(){o.ignore=o.normalIgnore,a.ignore=a.normalIgnore}Sg.call(this);var r=new zv({z2:2}),o=new Vv,a=new kv;this.add(r),this.add(o),this.add(a),this.updateData(t,e,!0),this.on("emphasis",n).on("normal",i).on("mouseover",n).on("mouseout",i)}function Qu(t,e,n,i,r,o,a){function s(e,n){for(var i=e;i>=0&&(t[i].y-=n,!(i>0&&t[i].y>t[i-1].y+t[i-1].height));i--);}function l(t,e,n,i,r,o){for(var a=e?Number.MAX_VALUE:0,s=0,l=t.length;s=a&&(d=a-10),!e&&d<=a&&(d=a+10),t[s].x=n+d*o,a=d}}t.sort(function(t,e){return t.y-e.y});for(var u,h=0,c=t.length,d=[],f=[],p=0;pe&&o+1t[o].y+t[o].height)return void s(o,i/2);s(n-1,i/2)}(p,c,-u),h=t[p].y+t[p].height;a-h<0&&s(c-1,h-a);for(p=0;p=n?f.push(t[p]):d.push(t[p]);l(d,!1,e,n,i,r),l(f,!0,e,n,i,r)}function Ju(t,e,n,i,r,o){for(var a=[],s=[],l=0;l=0&&s<0)&&(a=p,s=f,r=u,o.length=0),xb(h,function(t){o.push({seriesIndex:e.seriesIndex,dataIndexInside:t,dataIndex:e.getData().getRawIndex(t)})}))}}),{payloadBatch:o,snapToValue:r}}function hh(t,e,n,i){t[e.key]={value:n,payloadBatch:i}}function ch(t,e,n,i){var r=n.payloadBatch,o=e.axis,a=o.model,s=e.axisPointerModel;if(e.triggerTooltip&&r.length){var l=e.coordSys.model,u=Ru(l),h=t.map[u];h||(h=t.map[u]={coordSysId:l.id,coordSysIndex:l.componentIndex,coordSysType:l.type,coordSysMainType:l.mainType,dataByAxis:[]},t.list.push(h)),h.dataByAxis.push({axisDim:o.dim,axisIndex:a.componentIndex,axisType:a.type,axisId:a.id,value:i,valueLabelOpt:{precision:s.get("label.precision"),formatter:s.get("label.formatter")},seriesDataIndices:r.slice()})}}function dh(t,e,n){var i=n.axesInfo=[];xb(e,function(e,n){var r=e.axisPointerModel.option,o=t[n];o?(!e.useHandle&&(r.status="show"),r.value=o.value,r.seriesDataIndices=(o.payloadBatch||[]).slice()):!e.useHandle&&(r.status="hide"),"show"===r.status&&i.push({axisDim:e.axis.dim,axisIndex:e.axis.model.componentIndex,value:r.value})})}function fh(t,e,n,i){if(!vh(e)&&t.list.length){var r=((t.list[0].dataByAxis[0]||{}).seriesDataIndices||[])[0]||{};i({type:"showTip",escapeConnect:!0,x:e[0],y:e[1],tooltipOption:n.tooltipOption,position:n.position,dataIndexInside:r.dataIndexInside,dataIndex:r.dataIndex,seriesIndex:r.seriesIndex,dataByCoordSys:t.list})}else i({type:"hideTip"})}function ph(t,e,n){var i=n.getZr(),r=wb(i).axisPointerLastHighlights||{},o=wb(i).axisPointerLastHighlights={};xb(t,function(t,e){var n=t.axisPointerModel.option;"show"===n.status&&xb(n.seriesDataIndices,function(t){var e=t.seriesIndex+" | "+t.dataIndex;o[e]=t})});var a=[],s=[];d(r,function(t,e){!o[e]&&s.push(t)}),d(o,function(t,e){!r[e]&&a.push(t)}),s.length&&n.dispatchAction({type:"downplay",escapeConnect:!0,batch:s}),a.length&&n.dispatchAction({type:"highlight",escapeConnect:!0,batch:a})}function gh(t,e){for(var n=0;n<(t||[]).length;n++){var i=t[n];if(e.axis.dim===i.axisDim&&e.axis.model.componentIndex===i.axisIndex)return i}}function mh(t){var e=t.axis.model,n={},i=n.axisDim=t.axis.dim;return n.axisIndex=n[i+"AxisIndex"]=e.componentIndex,n.axisName=n[i+"AxisName"]=e.name,n.axisId=n[i+"AxisId"]=e.id,n}function vh(t){return!t||null==t[0]||isNaN(t[0])||null==t[1]||isNaN(t[1])}function yh(t,e,n){if(!bp.node){var i=e.getZr();bb(i).records||(bb(i).records={}),xh(i,e),(bb(i).records[t]||(bb(i).records[t]={})).handler=n}}function xh(t,e){function n(n,i){t.on(n,function(n){var r=Mh(e);Mb(bb(t).records,function(t){t&&i(t,n,r.dispatchAction)}),_h(r.pendings,e)})}bb(t).initialized||(bb(t).initialized=!0,n("click",v(bh,"click")),n("mousemove",v(bh,"mousemove")),n("globalout",wh))}function _h(t,e){var n,i=t.showTip.length,r=t.hideTip.length;i?n=t.showTip[i-1]:r&&(n=t.hideTip[r-1]),n&&(n.dispatchAction=null,e.dispatchAction(n))}function wh(t,e,n){t.handler("leave",null,n)}function bh(t,e,n,i){e.handler(t,n,i)}function Mh(t){var e={showTip:[],hideTip:[]},n=function(i){var r=e[i.type];r?r.push(i):(i.dispatchAction=n,t.dispatchAction(i))};return{dispatchAction:n,pendings:e}}function Sh(t,e){if(!bp.node){var n=e.getZr();(bb(n).records||{})[t]&&(bb(n).records[t]=null)}}function Ih(){}function Ch(t,e,n,i){Th(Ib(n).lastProp,i)||(Ib(n).lastProp=i,e?ar(n,i,t):(n.stopAnimation(),n.attr(i)))}function Th(t,e){if(w(t)&&w(e)){var n=!0;return d(e,function(e,i){n=n&&Th(t[i],e)}),!!n}return t===e}function Dh(t,e){t[e.get("label.show")?"show":"hide"]()}function Ah(t){return{position:t.position.slice(),rotation:t.rotation||0}}function kh(t,e,n){var i=e.get("z"),r=e.get("zlevel");t&&t.traverse(function(t){"group"!==t.type&&(null!=i&&(t.z=i),null!=r&&(t.zlevel=r),t.silent=n)})}function Ph(t){var e,n=t.get("type"),i=t.getModel(n+"Style");return"line"===n?(e=i.getLineStyle()).fill=null:"shadow"===n&&((e=i.getAreaStyle()).stroke=null),e}function Lh(t,e,n,i,r){var o=zh(n.get("value"),e.axis,e.ecModel,n.get("seriesDataIndices"),{precision:n.get("label.precision"),formatter:n.get("label.formatter")}),a=n.getModel("label"),s=cy(a.get("padding")||0),l=a.getFont(),u=ce(o,l),h=r.position,c=u.width+s[1]+s[3],d=u.height+s[0]+s[2],f=r.align;"right"===f&&(h[0]-=c),"center"===f&&(h[0]-=c/2);var p=r.verticalAlign;"bottom"===p&&(h[1]-=d),"middle"===p&&(h[1]-=d/2),Oh(h,c,d,i);var g=a.get("backgroundColor");g&&"auto"!==g||(g=e.get("axisLine.lineStyle.color")),t.label={shape:{x:0,y:0,width:c,height:d,r:a.get("borderRadius")},position:h.slice(),style:{text:o,textFont:l,textFill:a.getTextColor(),textPosition:"inside",fill:g,stroke:a.get("borderColor")||"transparent",lineWidth:a.get("borderWidth")||0,shadowBlur:a.get("shadowBlur"),shadowColor:a.get("shadowColor"),shadowOffsetX:a.get("shadowOffsetX"),shadowOffsetY:a.get("shadowOffsetY")},z2:10}}function Oh(t,e,n,i){var r=i.getWidth(),o=i.getHeight();t[0]=Math.min(t[0]+e,r)-e,t[1]=Math.min(t[1]+n,o)-n,t[0]=Math.max(t[0],0),t[1]=Math.max(t[1],0)}function zh(t,e,n,i,r){t=e.scale.parse(t);var o=e.scale.getLabel(t,{precision:r.precision}),a=r.formatter;if(a){var s={value:sl(e,t),seriesData:[]};d(i,function(t){var e=n.getSeriesByIndex(t.seriesIndex),i=t.dataIndexInside,r=e&&e.getDataParams(i);r&&s.seriesData.push(r)}),_(a)?o=a.replace("{value}",o):x(a)&&(o=a(s))}return o}function Eh(t,e,n){var i=rt();return ut(i,i,n.rotation),lt(i,i,n.position),ur([t.dataToCoord(e),(n.labelOffset||0)+(n.labelDirection||1)*(n.labelMargin||0)],i)}function Nh(t,e,n,i,r,o){var a=Xw.innerTextLayout(n.rotation,0,n.labelDirection);n.labelMargin=r.get("label.margin"),Lh(e,i,r,o,{position:Eh(i.axis,t,n),align:a.textAlign,verticalAlign:a.textVerticalAlign})}function Rh(t,e,n){return n=n||0,{x1:t[n],y1:t[1-n],x2:e[n],y2:e[1-n]}}function Bh(t,e,n){return n=n||0,{x:t[n],y:t[1-n],width:e[n],height:e[1-n]}}function Vh(t,e){var n={};return n[e.dim+"AxisIndex"]=e.index,t.getCartesian(n)}function Fh(t){return"x"===t.dim?0:1}function Hh(t){var e="left "+t+"s cubic-bezier(0.23, 1, 0.32, 1),top "+t+"s cubic-bezier(0.23, 1, 0.32, 1)";return f(Lb,function(t){return t+"transition:"+e}).join(";")}function Gh(t){var e=[],n=t.get("fontSize"),i=t.getTextColor();return i&&e.push("color:"+i),e.push("font:"+t.getFont()),n&&e.push("line-height:"+Math.round(3*n/2)+"px"),kb(["decoration","align"],function(n){var i=t.get(n);i&&e.push("text-"+n+":"+i)}),e.join(";")}function Wh(t){var e=[],n=t.get("transitionDuration"),i=t.get("backgroundColor"),r=t.getModel("textStyle"),o=t.get("padding");return n&&e.push(Hh(n)),i&&(bp.canvasSupported?e.push("background-Color:"+i):(e.push("background-Color:#"+Dt(i)),e.push("filter:alpha(opacity=70)"))),kb(["width","color","radius"],function(n){var i="border-"+n,r=Pb(i),o=t.get(r);null!=o&&e.push(i+":"+o+("color"===n?"":"px"))}),e.push(Gh(r)),null!=o&&e.push("padding:"+cy(o).join("px ")+"px"),e.join(";")+";"}function Zh(t,e){if(bp.wxa)return null;var n=document.createElement("div"),i=this._zr=e.getZr();this.el=n,this._x=e.getWidth()/2,this._y=e.getHeight()/2,t.appendChild(n),this._container=t,this._show=!1,this._hideTimeout;var r=this;n.onmouseenter=function(){r._enterable&&(clearTimeout(r._hideTimeout),r._show=!0),r._inContent=!0},n.onmousemove=function(e){if(e=e||window.event,!r._enterable){var n=i.handler;rn(t,e,!0),n.dispatch("mousemove",e)}},n.onmouseleave=function(){r._enterable&&r._show&&r.hideLater(r._hideDelay),r._inContent=!1}}function Uh(t){for(var e=t.pop();t.length;){var n=t.pop();n&&(pr.isInstance(n)&&(n=n.get("tooltip",!0)),"string"==typeof n&&(n={formatter:n}),e=new pr(n,e,e.ecModel))}return e}function Xh(t,e){return t.dispatchAction||m(e.dispatchAction,e)}function jh(t,e,n,i,r,o,a){var s=qh(n),l=s.width,u=s.height;return null!=o&&(t+l+o>i?t-=l+o:t+=o),null!=a&&(e+u+a>r?e-=u+a:e+=a),[t,e]}function Yh(t,e,n,i,r){var o=qh(n),a=o.width,s=o.height;return t=Math.min(t+a,i)-a,e=Math.min(e+s,r)-s,t=Math.max(t,0),e=Math.max(e,0),[t,e]}function qh(t){var e=t.clientWidth,n=t.clientHeight;if(document.defaultView&&document.defaultView.getComputedStyle){var i=document.defaultView.getComputedStyle(t);i&&(e+=parseInt(i.paddingLeft,10)+parseInt(i.paddingRight,10)+parseInt(i.borderLeftWidth,10)+parseInt(i.borderRightWidth,10),n+=parseInt(i.paddingTop,10)+parseInt(i.paddingBottom,10)+parseInt(i.borderTopWidth,10)+parseInt(i.borderBottomWidth,10))}return{width:e,height:n}}function $h(t,e,n){var i=n[0],r=n[1],o=0,a=0,s=e.width,l=e.height;switch(t){case"inside":o=e.x+s/2-i/2,a=e.y+l/2-r/2;break;case"top":o=e.x+s/2-i/2,a=e.y-r-5;break;case"bottom":o=e.x+s/2-i/2,a=e.y+l+5;break;case"left":o=e.x-i-5,a=e.y+l/2-r/2;break;case"right":o=e.x+s+5,a=e.y+l/2-r/2}return[o,a]}function Kh(t){return"center"===t||"middle"===t}function Qh(t,e,n){var i,r={},o="toggleSelected"===t;return n.eachComponent("legend",function(n){o&&null!=i?n[i?"select":"unSelect"](e.name):(n[t](e.name),i=n.isSelected(e.name)),d(n.getData(),function(t){var e=t.get("name");if("\n"!==e&&""!==e){var i=n.isSelected(e);r.hasOwnProperty(e)?r[e]=r[e]&&i:r[e]=i}})}),{name:e.name,selected:r}}function Jh(t,e,n){var i=e.getBoxLayoutParams(),r=e.get("padding"),o={width:n.getWidth(),height:n.getHeight()},a=Gr(i,o,r);by(e.get("orient"),t,e.get("itemGap"),a.width,a.height),Wr(t,i,o,r)}function tc(t,e){var n=cy(e.get("padding")),i=e.getItemStyle(["color","opacity"]);return i.fill=e.get("backgroundColor"),t=new Fv({shape:{x:t.x-n[3],y:t.y-n[0],width:t.width+n[1]+n[3],height:t.height+n[0]+n[2],r:e.get("borderRadius")},style:i,silent:!0,z2:-1})}function ec(t,e){e.dispatchAction({type:"legendToggleSelect",name:t})}function nc(t,e,n,i){var r=n.getZr().storage.getDisplayList()[0];r&&r.useHoverLayer||n.dispatchAction({type:"highlight",seriesName:t.name,name:e,excludeSeriesId:i})}function ic(t,e,n,i){var r=n.getZr().storage.getDisplayList()[0];r&&r.useHoverLayer||n.dispatchAction({type:"downplay",seriesName:t.name,name:e,excludeSeriesId:i})}function rc(t,e,n){var i=[1,1];i[t.getOrient().index]=0,Zr(e,n,{type:"box",ignoreSize:i})}function oc(t){_n(t,"label",["show"])}function ac(t){return!(isNaN(parseFloat(t.x))&&isNaN(parseFloat(t.y)))}function sc(t){return!isNaN(parseFloat(t.x))&&!isNaN(parseFloat(t.y))}function lc(t,e,n,i,r,o){var a=[],s=Ps(e,i)?e.getCalculationInfo("stackResultDimension"):i,l=pc(e,s,t),u=e.indicesOfNearest(s,l)[0];a[r]=e.get(n,u),a[o]=e.get(i,u);var h=Mr(e.get(i,u));return(h=Math.min(h,20))>=0&&(a[o]=+a[o].toFixed(h)),a}function uc(t,e){var i=t.getData(),r=t.coordinateSystem;if(e&&!sc(e)&&!y(e.coord)&&r){var o=r.dimensions,a=hc(e,i,r,t);if((e=n(e)).type&&Qb[e.type]&&a.baseAxis&&a.valueAxis){var s=$b(o,a.baseAxis.dim),l=$b(o,a.valueAxis.dim);e.coord=Qb[e.type](i,a.baseDataDim,a.valueDataDim,s,l),e.value=e.coord[l]}else{for(var u=[null!=e.xAxis?e.xAxis:e.radiusAxis,null!=e.yAxis?e.yAxis:e.angleAxis],h=0;h<2;h++)Qb[u[h]]&&(u[h]=pc(i,i.mapDimension(o[h]),u[h]));e.coord=u}}return e}function hc(t,e,n,i){var r={};return null!=t.valueIndex||null!=t.valueDim?(r.valueDataDim=null!=t.valueIndex?e.getDimension(t.valueIndex):t.valueDim,r.valueAxis=n.getAxis(cc(i,r.valueDataDim)),r.baseAxis=n.getOtherAxis(r.valueAxis),r.baseDataDim=e.mapDimension(r.baseAxis.dim)):(r.baseAxis=i.getBaseAxis(),r.valueAxis=n.getOtherAxis(r.baseAxis),r.baseDataDim=e.mapDimension(r.baseAxis.dim),r.valueDataDim=e.mapDimension(r.valueAxis.dim)),r}function cc(t,e){var n=t.getData(),i=n.dimensions;e=n.getDimension(e);for(var r=0;r=0}function Fc(t,e,n){function i(t,e){return l(e.nodes,t)>=0}function r(t,i){var r=!1;return e(function(e){d(n(t,e)||[],function(t){i.records[e.name][t]&&(r=!0)})}),r}function o(t,i){i.nodes.push(t),e(function(e){d(n(t,e)||[],function(t){i.records[e.name][t]=!0})})}return function(n){var a={nodes:[],records:{}};if(e(function(t){a.records[t.name]={}}),!n)return a;o(n,a);var s;do{s=!1,t(function(t){!i(t,a)&&r(t,a)&&(o(t,a),s=!0)})}while(s);return a}}function Hc(t,e,n){var i=[1/0,-1/0];return cM(n,function(t){var n=t.getData();n&&cM(n.mapDimension(e,!0),function(t){var e=n.getApproximateExtent(t);e[0]i[1]&&(i[1]=e[1])})}),i[1]0?0:NaN);var a=n.getMax(!0);return null!=a&&"dataMax"!==a&&"function"!=typeof a?e[1]=a:r&&(e[1]=o>0?o-1:NaN),n.get("scale",!0)||(e[0]>0&&(e[0]=0),e[1]<0&&(e[1]=0)),e}function Wc(t,e){var n=t.getAxisModel(),i=t._percentWindow,r=t._valueWindow;if(i){var o=Ir(r,[0,500]);o=Math.min(o,20);var a=e||0===i[0]&&100===i[1];n.setRange(a?null:+r[0].toFixed(o),a?null:+r[1].toFixed(o))}}function Zc(t){var e=t._minMaxSpan={},n=t._dataZoomModel;cM(["min","max"],function(i){e[i+"Span"]=n.get(i+"Span");var r=n.get(i+"ValueSpan");if(null!=r&&(e[i+"ValueSpan"]=r,null!=(r=t.getAxisModel().axis.scale.parse(r)))){var o=t._dataExtent;e[i+"Span"]=xr(o[0]+r,o,[0,100],!0)}})}function Uc(t){var e={};return pM(["start","end","startValue","endValue","throttle"],function(n){t.hasOwnProperty(n)&&(e[n]=t[n])}),e}function Xc(t,e){var n=t._rangePropMode,i=t.get("rangeMode");pM([["start","startValue"],["end","endValue"]],function(t,r){var o=null!=e[t[0]],a=null!=e[t[1]];o&&!a?n[r]="percent":!o&&a?n[r]="value":i?n[r]=i[r]:o&&(n[r]="percent")})}function jc(t,e){var n=t[e]-t[1-e];return{span:Math.abs(n),sign:n>0?-1:n<0?1:e?-1:1}}function Yc(t,e){return Math.min(e[1],Math.max(e[0],t))}function qc(t){return{x:"y",y:"x",radius:"angle",angle:"radius"}[t]}function $c(t){return"vertical"===t?"ns-resize":"ew-resize"}function Kc(t,e,n){td(t)[e]=n}function Qc(t,e,n){var i=td(t);i[e]===n&&(i[e]=null)}function Jc(t,e){return!!td(t)[e]}function td(t){return t[DM]||(t[DM]={})}function ed(t){this.pointerChecker,this._zr=t,this._opt={};var e=m,i=e(nd,this),r=e(id,this),o=e(rd,this),s=e(od,this),l=e(ad,this);Zp.call(this),this.setPointerChecker=function(t){this.pointerChecker=t},this.enable=function(e,u){this.disable(),this._opt=a(n(u)||{},{zoomOnMouseWheel:!0,moveOnMouseMove:!0,preventDefaultMouseMove:!0}),null==e&&(e=!0),!0!==e&&"move"!==e&&"pan"!==e||(t.on("mousedown",i),t.on("mousemove",r),t.on("mouseup",o)),!0!==e&&"scale"!==e&&"zoom"!==e||(t.on("mousewheel",s),t.on("pinch",l))},this.disable=function(){t.off("mousedown",i),t.off("mousemove",r),t.off("mouseup",o),t.off("mousewheel",s),t.off("pinch",l)},this.dispose=this.disable,this.isDragging=function(){return this._dragging},this.isPinching=function(){return this._pinching}}function nd(t){if(!(sn(t)||t.target&&t.target.draggable)){var e=t.offsetX,n=t.offsetY;this.pointerChecker&&this.pointerChecker(t,e,n)&&(this._x=e,this._y=n,this._dragging=!0)}}function id(t){if(!sn(t)&&ld(this,"moveOnMouseMove",t)&&this._dragging&&"pinch"!==t.gestureEvent&&!Jc(this._zr,"globalPan")){var e=t.offsetX,n=t.offsetY,i=this._x,r=this._y,o=e-i,a=n-r;this._x=e,this._y=n,this._opt.preventDefaultMouseMove&&tm(t.event),this.trigger("pan",o,a,i,r,e,n)}}function rd(t){sn(t)||(this._dragging=!1)}function od(t){if(ld(this,"zoomOnMouseWheel",t)&&0!==t.wheelDelta){var e=t.wheelDelta>0?1.1:1/1.1;sd.call(this,t,e,t.offsetX,t.offsetY)}}function ad(t){if(!Jc(this._zr,"globalPan")){var e=t.pinchScale>1?1.1:1/1.1;sd.call(this,t,e,t.pinchX,t.pinchY)}}function sd(t,e,n,i){this.pointerChecker&&this.pointerChecker(t,n,i)&&(tm(t.event),this.trigger("zoom",e,n,i))}function ld(t,e,n){var i=t._opt[e];return i&&(!_(i)||n.event[i+"Key"])}function ud(t,e){var n=dd(t),i=e.dataZoomId,r=e.coordId;d(n,function(t,n){var o=t.dataZoomInfos;o[i]&&l(e.allCoordIds,r)<0&&(delete o[i],t.count--)}),pd(n);var o=n[r];o||((o=n[r]={coordId:r,dataZoomInfos:{},count:0}).controller=fd(t,o),o.dispatchAction=v(yd,t)),!o.dataZoomInfos[i]&&o.count++,o.dataZoomInfos[i]=e;var a=xd(o.dataZoomInfos);o.controller.enable(a.controlType,a.opt),o.controller.setPointerChecker(e.containsPoint),ha(o,"dispatchAction",e.throttleRate,"fixRate")}function hd(t,e){var n=dd(t);d(n,function(t){t.controller.dispose();var n=t.dataZoomInfos;n[e]&&(delete n[e],t.count--)}),pd(n)}function cd(t){return t.type+"\0_"+t.id}function dd(t){var e=t.getZr();return e[kM]||(e[kM]={})}function fd(t,e){var n=new ed(t.getZr());return n.on("pan",AM(gd,e)),n.on("zoom",AM(md,e)),n}function pd(t){d(t,function(e,n){e.count||(e.controller.dispose(),delete t[n])})}function gd(t,e,n,i,r,o,a){vd(t,function(s){return s.panGetRange(t.controller,e,n,i,r,o,a)})}function md(t,e,n,i){vd(t,function(r){return r.zoomGetRange(t.controller,e,n,i)})}function vd(t,e){var n=[];d(t.dataZoomInfos,function(t){var i=e(t);!t.disabled&&i&&n.push({dataZoomId:t.dataZoomId,start:i[0],end:i[1]})}),n.length&&t.dispatchAction(n)}function yd(t,e){t.dispatchAction({type:"dataZoom",batch:e})}function xd(t){var e,n={},i={type_true:2,type_move:1,type_false:0,type_undefined:-1};return d(t,function(t){var r=!t.disabled&&(!t.zoomLock||"move");i["type_"+r]>i["type_"+e]&&(e=r),o(n,t.roamControllerOpt)}),{controlType:e,opt:n}}function _d(t,e){zM[t]=e}function wd(t){return zM[t]}function bd(t){return 0===t.indexOf("my")}function Md(t){this.model=t}function Sd(t){this.model=t}function Id(t){var e={},n=[],i=[];return t.eachRawSeries(function(t){var r=t.coordinateSystem;if(!r||"cartesian2d"!==r.type&&"polar"!==r.type)n.push(t);else{var o=r.getBaseAxis();if("category"===o.type){var a=o.dim+"_"+o.index;e[a]||(e[a]={categoryAxis:o,valueAxis:r.getOtherAxis(o),series:[]},i.push({axisDim:o.dim,axisIndex:o.index})),e[a].series.push(t)}else n.push(t)}}),{seriesGroupByCategoryAxis:e,other:n,meta:i}}function Cd(t){var e=[];return d(t,function(t,n){var i=t.categoryAxis,r=t.valueAxis.dim,o=[" "].concat(f(t.series,function(t){return t.name})),a=[i.model.getCategories()];d(t.series,function(t){a.push(t.getRawData().mapArray(r,function(t){return t}))});for(var s=[o.join(WM)],l=0;l=0)return!0}function Pd(t){for(var e=t.split(/\n+/g),n=[],i=f(Ad(e.shift()).split(ZM),function(t){return{name:t,data:[]}}),r=0;rQM}function $d(t){var e=t.length-1;return e<0&&(e=0),[t[0],t[e]]}function Kd(t,e,n,i){var r=new Sg;return r.add(new Fv({name:"main",style:ef(n),silent:!0,draggable:!0,cursor:"move",drift:UM(t,e,r,"nswe"),ondragend:UM(Yd,e,{isEnd:!0})})),XM(i,function(n){r.add(new Fv({name:n,style:{opacity:0},draggable:!0,silent:!0,invisible:!0,drift:UM(t,e,r,n),ondragend:UM(Yd,e,{isEnd:!0})}))}),r}function Qd(t,e,n,i){var r=i.brushStyle.lineWidth||0,o=qM(r,JM),a=n[0][0],s=n[1][0],l=a-r/2,u=s-r/2,h=n[0][1],c=n[1][1],d=h-o+r/2,f=c-o+r/2,p=h-a,g=c-s,m=p+r,v=g+r;tf(t,e,"main",a,s,p,g),i.transformable&&(tf(t,e,"w",l,u,o,v),tf(t,e,"e",d,u,o,v),tf(t,e,"n",l,u,m,o),tf(t,e,"s",l,f,m,o),tf(t,e,"nw",l,u,o,o),tf(t,e,"ne",d,u,o,o),tf(t,e,"sw",l,f,o,o),tf(t,e,"se",d,f,o,o))}function Jd(t,e){var n=e.__brushOption,i=n.transformable,r=e.childAt(0);r.useStyle(ef(n)),r.attr({silent:!i,cursor:i?"move":"default"}),XM(["w","e","n","s","se","sw","ne","nw"],function(n){var r=e.childOfName(n),o=of(t,n);r&&r.attr({silent:!i,invisible:!i,cursor:i?nS[o]+"-resize":null})})}function tf(t,e,n,i,r,o,a){var s=e.childOfName(n);s&&s.setShape(hf(uf(t,e,[[i,r],[i+o,r+a]])))}function ef(t){return a({strokeNoScale:!0},t.brushStyle)}function nf(t,e,n,i){var r=[YM(t,n),YM(e,i)],o=[qM(t,n),qM(e,i)];return[[r[0],o[0]],[r[1],o[1]]]}function rf(t){return lr(t.group)}function of(t,e){if(e.length>1)return("e"===(i=[of(t,(e=e.split(""))[0]),of(t,e[1])])[0]||"w"===i[0])&&i.reverse(),i.join("");var n={left:"w",right:"e",top:"n",bottom:"s"},i=hr({w:"left",e:"right",n:"top",s:"bottom"}[e],rf(t));return n[i]}function af(t,e,n,i,r,o,a,s){var l=i.__brushOption,u=t(l.range),h=lf(n,o,a);XM(r.split(""),function(t){var e=eS[t];u[e[0]][e[1]]+=h[e[0]]}),l.range=e(nf(u[0][0],u[1][0],u[0][1],u[1][1])),Wd(n,i),Yd(n,{isEnd:!1})}function sf(t,e,n,i,r){var o=e.__brushOption.range,a=lf(t,n,i);XM(o,function(t){t[0]+=a[0],t[1]+=a[1]}),Wd(t,e),Yd(t,{isEnd:!1})}function lf(t,e,n){var i=t.group,r=i.transformCoordToLocal(e,n),o=i.transformCoordToLocal(0,0);return[r[0]-o[0],r[1]-o[1]]}function uf(t,e,i){var r=Xd(t,e);return r&&!0!==r?r.clipPath(i,t._transform):n(i)}function hf(t){var e=YM(t[0][0],t[1][0]),n=YM(t[0][1],t[1][1]);return{x:e,y:n,width:qM(t[0][0],t[1][0])-e,height:qM(t[0][1],t[1][1])-n}}function cf(t,e,n){if(t._brushType){var i=t._zr,r=t._covers,o=Ud(t,e,n);if(!t._dragging)for(var a=0;a=0)&&t(o,i,r)})}function Sf(t){return t[0]>t[1]&&t.reverse(),t}function If(t,e){return An(t,e,{includeMainTypes:dS})}function Cf(t,e,n,i){var r=n.getAxis(["x","y"][t]),o=Sf(f([0,1],function(t){return e?r.coordToData(r.toLocalCoord(i[t])):r.toGlobalCoord(r.dataToCoord(i[t]))})),a=[];return a[t]=o,a[1-t]=[NaN,NaN],{values:o,xyMinMax:a}}function Tf(t,e,n,i){return[e[0]-i[t]*n[0],e[1]-i[t]*n[1]]}function Df(t,e){var n=Af(t),i=Af(e),r=[n[0]/i[0],n[1]/i[1]];return isNaN(r[0])&&(r[0]=1),isNaN(r[1])&&(r[1]=1),r}function Af(t){return t?[t[0][1]-t[0][0],t[1][1]-t[1][0]]:[NaN,NaN]}function kf(t,e){var n=zf(t);xS(e,function(e,i){for(var r=n.length-1;r>=0&&!n[r][i];r--);if(r<0){var o=t.queryComponents({mainType:"dataZoom",subType:"select",id:i})[0];if(o){var a=o.getPercentRange();n[0][i]={dataZoomId:i,start:a[0],end:a[1]}}}}),n.push(e)}function Pf(t){var e=zf(t),n=e[e.length-1];e.length>1&&e.pop();var i={};return xS(n,function(t,n){for(var r=e.length-1;r>=0;r--)if(t=e[r][n]){i[n]=t;break}}),i}function Lf(t){t[_S]=null}function Of(t){return zf(t).length}function zf(t){var e=t[_S];return e||(e=t[_S]=[{}]),e}function Ef(t,e,n){(this._brushController=new Nd(n.getZr())).on("brush",m(this._onBrush,this)).mount(),this._isZoomActive}function Nf(t){var e={};return d(["xAxisIndex","yAxisIndex"],function(n){e[n]=t[n],null==e[n]&&(e[n]="all"),(!1===e[n]||"none"===e[n])&&(e[n]=[])}),e}function Rf(t,e){t.setIconStatus("back",Of(e)>1?"emphasis":"normal")}function Bf(t,e,n,i,r){var o=n._isZoomActive;i&&"takeGlobalCursor"===i.type&&(o="dataZoomSelect"===i.key&&i.dataZoomSelectActive),n._isZoomActive=o,t.setIconStatus("zoom",o?"emphasis":"normal");var a=new Mf(Nf(t.option),e,{include:["grid"]});n._brushController.setPanels(a.makePanelOpts(r,function(t){return t.xAxisDeclared&&!t.yAxisDeclared?"lineX":!t.xAxisDeclared&&t.yAxisDeclared?"lineY":"rect"})).enableBrush(!!o&&{brushType:"auto",brushStyle:{lineWidth:0,fill:"rgba(0,0,0,0.2)"}})}function Vf(t){this.model=t}function Ff(t){return TS(t)}function Hf(){if(!kS&&PS){kS=!0;var t=PS.styleSheets;t.length<31?PS.createStyleSheet().addRule(".zrvml","behavior:url(#default#VML)"):t[0].addRule(".zrvml","behavior:url(#default#VML)")}}function Gf(t){return parseInt(t,10)}function Wf(t,e){Hf(),this.root=t,this.storage=e;var n=document.createElement("div"),i=document.createElement("div");n.style.cssText="display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;",i.style.cssText="position:absolute;left:0;top:0;",t.appendChild(n),this._vmlRoot=i,this._vmlViewport=n,this.resize();var r=e.delFromStorage,o=e.addToStorage;e.delFromStorage=function(t){r.call(e,t),t&&t.onRemove&&t.onRemove(i)},e.addToStorage=function(t){t.onAdd&&t.onAdd(i),o.call(e,t)},this._firstPaint=!0}function Zf(t){return function(){yg('In IE8.0 VML mode painter not support method "'+t+'"')}}function Uf(t){return document.createElementNS(cI,t)}function Xf(t){return gI(1e4*t)/1e4}function jf(t){return t-wI}function Yf(t,e){var n=e?t.textFill:t.fill;return null!=n&&n!==pI}function qf(t,e){var n=e?t.textStroke:t.stroke;return null!=n&&n!==pI}function $f(t,e){e&&Kf(t,"transform","matrix("+fI.call(e,",")+")")}function Kf(t,e,n){(!n||"linear"!==n.type&&"radial"!==n.type)&&("string"==typeof n&&n.indexOf("NaN")>-1&&console.log(n),t.setAttribute(e,n))}function Qf(t,e,n){t.setAttributeNS("http://www.w3.org/1999/xlink",e,n)}function Jf(t,e,n){if(Yf(e,n)){var i=n?e.textFill:e.fill;i="transparent"===i?pI:i,"none"!==t.getAttribute("clip-path")&&i===pI&&(i="rgba(0, 0, 0, 0.002)"),Kf(t,"fill",i),Kf(t,"fill-opacity",e.opacity)}else Kf(t,"fill",pI);if(qf(e,n)){var r=n?e.textStroke:e.stroke;Kf(t,"stroke",r="transparent"===r?pI:r),Kf(t,"stroke-width",(n?e.textStrokeWidth:e.lineWidth)/(!n&&e.strokeNoScale?e.host.getLineScale():1)),Kf(t,"paint-order",n?"stroke":"fill"),Kf(t,"stroke-opacity",e.opacity),e.lineDash?(Kf(t,"stroke-dasharray",e.lineDash.join(",")),Kf(t,"stroke-dashoffset",gI(e.lineDashOffset||0))):Kf(t,"stroke-dasharray",""),e.lineCap&&Kf(t,"stroke-linecap",e.lineCap),e.lineJoin&&Kf(t,"stroke-linejoin",e.lineJoin),e.miterLimit&&Kf(t,"stroke-miterlimit",e.miterLimit)}else Kf(t,"stroke",pI)}function tp(t){for(var e=[],n=t.data,i=t.len(),r=0;r=xI||!jf(g)&&(d>-yI&&d<0||d>yI)==!!p;var y=Xf(s+u*vI(c)),x=Xf(l+h*mI(c));m&&(d=p?xI-1e-4:1e-4-xI,v=!0,9===r&&e.push("M",y,x));var _=Xf(s+u*vI(c+d)),w=Xf(l+h*mI(c+d));e.push("A",Xf(u),Xf(h),gI(f*_I),+v,+p,_,w);break;case dI.Z:o="Z";break;case dI.R:var _=Xf(n[r++]),w=Xf(n[r++]),b=Xf(n[r++]),M=Xf(n[r++]);e.push("M",_,w,"L",_+b,w,"L",_+b,w+M,"L",_,w+M,"L",_,w)}o&&e.push(o);for(var S=0;S=11)}}(navigator.userAgent),Mp={"[object Function]":1,"[object RegExp]":1,"[object Date]":1,"[object Error]":1,"[object CanvasGradient]":1,"[object CanvasPattern]":1,"[object Image]":1,"[object Canvas]":1},Sp={"[object Int8Array]":1,"[object Uint8Array]":1,"[object Uint8ClampedArray]":1,"[object Int16Array]":1,"[object Uint16Array]":1,"[object Int32Array]":1,"[object Uint32Array]":1,"[object Float32Array]":1,"[object Float64Array]":1},Ip=Object.prototype.toString,Cp=Array.prototype,Tp=Cp.forEach,Dp=Cp.filter,Ap=Cp.slice,kp=Cp.map,Pp=Cp.reduce,Lp={},Op=function(){return Lp.createCanvas()};Lp.createCanvas=function(){return document.createElement("canvas")};var zp,Ep="__ec_primitive__";E.prototype={constructor:E,get:function(t){return this.hasOwnProperty(t)?this[t]:null},set:function(t,e){return this[t]=e},each:function(t,e){void 0!==e&&(t=m(t,e));for(var n in this)this.hasOwnProperty(n)&&t(this[n],n)},removeKey:function(t){delete this[t]}};var Np=(Object.freeze||Object)({$override:e,clone:n,merge:i,mergeAll:r,extend:o,defaults:a,createCanvas:Op,getContext:s,indexOf:l,inherits:u,mixin:h,isArrayLike:c,each:d,map:f,reduce:p,filter:g,find:function(t,e,n){if(t&&e)for(var i=0,r=t.length;i3&&(e=Wp.call(e,1));for(var i=this._$handlers[t],r=i.length,o=0;o4&&(e=Wp.call(e,1,e.length-1));for(var i=e[e.length-1],r=this._$handlers[t],o=r.length,a=0;a=0;o--){var a;if(i[o]!==n&&!i[o].ignore&&(a=it(i[o],t,e))&&(!r.topTarget&&(r.topTarget=i[o]),a!==Up)){r.target=i[o];break}}return r}},d(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],function(t){jp.prototype[t]=function(e){var n=this.findHover(e.zrX,e.zrY),i=n.target;if("mousedown"===t)this._downEl=i,this._downPoint=[e.zrX,e.zrY],this._upEl=i;else if("mouseup"===t)this._upEl=i;else if("click"===t){if(this._downEl!==this._upEl||!this._downPoint||Fp(this._downPoint,[e.zrX,e.zrY])>4)return;this._downPoint=null}this.dispatchToElement(n,t,e)}}),h(jp,Zp),h(jp,J);var Yp="undefined"==typeof Float32Array?Array:Float32Array,qp=(Object.freeze||Object)({create:rt,identity:ot,copy:at,mul:st,translate:lt,rotate:ut,scale:ht,invert:ct,clone:function(t){var e=rt();return at(e,t),e}}),$p=ot,Kp=5e-5,Qp=function(t){(t=t||{}).position||(this.position=[0,0]),null==t.rotation&&(this.rotation=0),t.scale||(this.scale=[1,1]),this.origin=this.origin||null},Jp=Qp.prototype;Jp.transform=null,Jp.needLocalTransform=function(){return dt(this.rotation)||dt(this.position[0])||dt(this.position[1])||dt(this.scale[0]-1)||dt(this.scale[1]-1)},Jp.updateTransform=function(){var t=this.parent,e=t&&t.transform,n=this.needLocalTransform(),i=this.transform;n||e?(i=i||rt(),n?this.getLocalTransform(i):$p(i),e&&(n?st(i,t.transform,i):at(i,t.transform)),this.transform=i,this.invTransform=this.invTransform||rt(),ct(this.invTransform,i)):i&&$p(i)},Jp.getLocalTransform=function(t){return Qp.getLocalTransform(this,t)},Jp.setTransform=function(t){var e=this.transform,n=t.dpr||1;e?t.setTransform(n*e[0],n*e[1],n*e[2],n*e[3],n*e[4],n*e[5]):t.setTransform(n,0,0,n,0,0)},Jp.restoreTransform=function(t){var e=t.dpr||1;t.setTransform(e,0,0,e,0,0)};var tg=[];Jp.decomposeTransform=function(){if(this.transform){var t=this.parent,e=this.transform;t&&t.transform&&(st(tg,t.invTransform,e),e=tg);var n=e[0]*e[0]+e[1]*e[1],i=e[2]*e[2]+e[3]*e[3],r=this.position,o=this.scale;dt(n-1)&&(n=Math.sqrt(n)),dt(i-1)&&(i=Math.sqrt(i)),e[0]<0&&(n=-n),e[3]<0&&(i=-i),r[0]=e[4],r[1]=e[5],o[0]=n,o[1]=i,this.rotation=Math.atan2(-e[1]/i,e[0]/n)}},Jp.getGlobalScale=function(){var t=this.transform;if(!t)return[1,1];var e=Math.sqrt(t[0]*t[0]+t[1]*t[1]),n=Math.sqrt(t[2]*t[2]+t[3]*t[3]);return t[0]<0&&(e=-e),t[3]<0&&(n=-n),[e,n]},Jp.transformCoordToLocal=function(t,e){var n=[t,e],i=this.invTransform;return i&&$(n,n,i),n},Jp.transformCoordToGlobal=function(t,e){var n=[t,e],i=this.transform;return i&&$(n,n,i),n},Qp.getLocalTransform=function(t,e){$p(e=e||[]);var n=t.origin,i=t.scale||[1,1],r=t.rotation||0,o=t.position||[0,0];return n&&(e[4]-=n[0],e[5]-=n[1]),ht(e,e,i),r&&ut(e,e,r),n&&(e[4]+=n[0],e[5]+=n[1]),e[4]+=o[0],e[5]+=o[1],e};var eg={linear:function(t){return t},quadraticIn:function(t){return t*t},quadraticOut:function(t){return t*(2-t)},quadraticInOut:function(t){return(t*=2)<1?.5*t*t:-.5*(--t*(t-2)-1)},cubicIn:function(t){return t*t*t},cubicOut:function(t){return--t*t*t+1},cubicInOut:function(t){return(t*=2)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},quarticIn:function(t){return t*t*t*t},quarticOut:function(t){return 1- --t*t*t*t},quarticInOut:function(t){return(t*=2)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},quinticIn:function(t){return t*t*t*t*t},quinticOut:function(t){return--t*t*t*t*t+1},quinticInOut:function(t){return(t*=2)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},sinusoidalIn:function(t){return 1-Math.cos(t*Math.PI/2)},sinusoidalOut:function(t){return Math.sin(t*Math.PI/2)},sinusoidalInOut:function(t){return.5*(1-Math.cos(Math.PI*t))},exponentialIn:function(t){return 0===t?0:Math.pow(1024,t-1)},exponentialOut:function(t){return 1===t?1:1-Math.pow(2,-10*t)},exponentialInOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(2-Math.pow(2,-10*(t-1)))},circularIn:function(t){return 1-Math.sqrt(1-t*t)},circularOut:function(t){return Math.sqrt(1- --t*t)},circularInOut:function(t){return(t*=2)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},elasticIn:function(t){var e,n=.1;return 0===t?0:1===t?1:(!n||n<1?(n=1,e=.1):e=.4*Math.asin(1/n)/(2*Math.PI),-n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4))},elasticOut:function(t){var e,n=.1;return 0===t?0:1===t?1:(!n||n<1?(n=1,e=.1):e=.4*Math.asin(1/n)/(2*Math.PI),n*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/.4)+1)},elasticInOut:function(t){var e,n=.1;return 0===t?0:1===t?1:(!n||n<1?(n=1,e=.1):e=.4*Math.asin(1/n)/(2*Math.PI),(t*=2)<1?n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4)*-.5:n*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4)*.5+1)},backIn:function(t){var e=1.70158;return t*t*((e+1)*t-e)},backOut:function(t){var e=1.70158;return--t*t*((e+1)*t+e)+1},backInOut:function(t){var e=2.5949095;return(t*=2)<1?t*t*((e+1)*t-e)*.5:.5*((t-=2)*t*((e+1)*t+e)+2)},bounceIn:function(t){return 1-eg.bounceOut(1-t)},bounceOut:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},bounceInOut:function(t){return t<.5?.5*eg.bounceIn(2*t):.5*eg.bounceOut(2*t-1)+.5}};ft.prototype={constructor:ft,step:function(t,e){if(this._initialized||(this._startTime=t+this._delay,this._initialized=!0),this._paused)this._pausedTime+=e;else{var n=(t-this._startTime-this._pausedTime)/this._life;if(!(n<0)){n=Math.min(n,1);var i=this.easing,r="string"==typeof i?eg[i]:i,o="function"==typeof r?r(n):n;return this.fire("frame",o),1==n?this.loop?(this.restart(t),"restart"):(this._needsRemove=!0,"destroy"):null}}},restart:function(t){var e=(t-this._startTime-this._pausedTime)%this._life;this._startTime=t-e+this.gap,this._pausedTime=0,this._needsRemove=!1},fire:function(t,e){this[t="on"+t]&&this[t](this._target,e)},pause:function(){this._paused=!0},resume:function(){this._paused=!1}};var ng=function(){this.head=null,this.tail=null,this._len=0},ig=ng.prototype;ig.insert=function(t){var e=new rg(t);return this.insertEntry(e),e},ig.insertEntry=function(t){this.head?(this.tail.next=t,t.prev=this.tail,t.next=null,this.tail=t):this.head=this.tail=t,this._len++},ig.remove=function(t){var e=t.prev,n=t.next;e?e.next=n:this.head=n,n?n.prev=e:this.tail=e,t.next=t.prev=null,this._len--},ig.len=function(){return this._len},ig.clear=function(){this.head=this.tail=null,this._len=0};var rg=function(t){this.value=t,this.next,this.prev},og=function(t){this._list=new ng,this._map={},this._maxSize=t||10,this._lastRemovedEntry=null},ag=og.prototype;ag.put=function(t,e){var n=this._list,i=this._map,r=null;if(null==i[t]){var o=n.len(),a=this._lastRemovedEntry;if(o>=this._maxSize&&o>0){var s=n.head;n.remove(s),delete i[s.key],r=s.value,this._lastRemovedEntry=s}a?a.value=e:a=new rg(e),a.key=t,n.insertEntry(a),i[t]=a}return r},ag.get=function(t){var e=this._map[t],n=this._list;if(null!=e)return e!==n.tail&&(n.remove(e),n.insertEntry(e)),e.value},ag.clear=function(){this._list.clear(),this._map={}};var sg={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]},lg=new og(20),ug=null,hg=At,cg=kt,dg=(Object.freeze||Object)({parse:St,lift:Tt,toHex:Dt,fastLerp:At,fastMapToColor:hg,lerp:kt,mapToColor:cg,modifyHSL:function(t,e,n,i){if(t=St(t))return t=Ct(t),null!=e&&(t[0]=gt(e)),null!=n&&(t[1]=yt(n)),null!=i&&(t[2]=yt(i)),Lt(It(t),"rgba")},modifyAlpha:Pt,stringify:Lt}),fg=Array.prototype.slice,pg=function(t,e,n,i){this._tracks={},this._target=t,this._loop=e||!1,this._getter=n||Ot,this._setter=i||zt,this._clipCount=0,this._delay=0,this._doneList=[],this._onframeList=[],this._clipList=[]};pg.prototype={when:function(t,e){var n=this._tracks;for(var i in e)if(e.hasOwnProperty(i)){if(!n[i]){n[i]=[];var r=this._getter(this._target,i);if(null==r)continue;0!==t&&n[i].push({time:0,value:Gt(r)})}n[i].push({time:t,value:e[i]})}return this},during:function(t){return this._onframeList.push(t),this},pause:function(){for(var t=0;t0&&this.animate(t,!1).when(null==i?500:i,o).delay(r||0),this}};var _g=function(t){Qp.call(this,t),Zp.call(this,t),xg.call(this,t),this.id=t.id||_p()};_g.prototype={type:"element",name:"",__zr:null,ignore:!1,clipPath:null,isGroup:!1,drift:function(t,e){switch(this.draggable){case"horizontal":e=0;break;case"vertical":t=0}var n=this.transform;n||(n=this.transform=[1,0,0,1,0,0]),n[4]+=t,n[5]+=e,this.decomposeTransform(),this.dirty(!1)},beforeUpdate:function(){},afterUpdate:function(){},update:function(){this.updateTransform()},traverse:function(t,e){},attrKV:function(t,e){if("position"===t||"scale"===t||"origin"===t){if(e){var n=this[t];n||(n=this[t]=[]),n[0]=e[0],n[1]=e[1]}}else this[t]=e},hide:function(){this.ignore=!0,this.__zr&&this.__zr.refresh()},show:function(){this.ignore=!1,this.__zr&&this.__zr.refresh()},attr:function(t,e){if("string"==typeof t)this.attrKV(t,e);else if(w(t))for(var n in t)t.hasOwnProperty(n)&&this.attrKV(n,t[n]);return this.dirty(!1),this},setClipPath:function(t){var e=this.__zr;e&&t.addSelfToZr(e),this.clipPath&&this.clipPath!==t&&this.removeClipPath(),this.clipPath=t,t.__zr=e,t.__clipTarget=this,this.dirty(!1)},removeClipPath:function(){var t=this.clipPath;t&&(t.__zr&&t.removeSelfFromZr(t.__zr),t.__zr=null,t.__clipTarget=null,this.clipPath=null,this.dirty(!1))},addSelfToZr:function(t){this.__zr=t;var e=this.animators;if(e)for(var n=0;n=n.x&&t<=n.x+n.width&&e>=n.y&&e<=n.y+n.height},clone:function(){return new Xt(this.x,this.y,this.width,this.height)},copy:function(t){this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height},plain:function(){return{x:this.x,y:this.y,width:this.width,height:this.height}}},Xt.create=function(t){return new Xt(t.x,t.y,t.width,t.height)};var Sg=function(t){t=t||{},_g.call(this,t);for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);this._children=[],this.__storage=null,this.__dirty=!0};Sg.prototype={constructor:Sg,isGroup:!0,type:"group",silent:!1,children:function(){return this._children.slice()},childAt:function(t){return this._children[t]},childOfName:function(t){for(var e=this._children,n=0;n=0&&(n.splice(i,0,t),this._doAdd(t))}return this},_doAdd:function(t){t.parent&&t.parent.remove(t),t.parent=this;var e=this.__storage,n=this.__zr;e&&e!==t.__storage&&(e.addToStorage(t),t instanceof Sg&&t.addChildrenToStorage(e)),n&&n.refresh()},remove:function(t){var e=this.__zr,n=this.__storage,i=this._children,r=l(i,t);return r<0?this:(i.splice(r,1),t.parent=null,n&&(n.delFromStorage(t),t instanceof Sg&&t.delChildrenFromStorage(n)),e&&e.refresh(),this)},removeAll:function(){var t,e,n=this._children,i=this.__storage;for(e=0;e=0&&(this.delFromStorage(t),this._roots.splice(r,1),t instanceof Sg&&t.delChildrenFromStorage(this))}},addToStorage:function(t){return t&&(t.__storage=this,t.dirty(!1)),this},delFromStorage:function(t){return t&&(t.__storage=null),this},dispose:function(){this._renderList=this._roots=null},displayableSortFunc:ee};var Dg={shadowBlur:1,shadowOffsetX:1,shadowOffsetY:1,textShadowBlur:1,textShadowOffsetX:1,textShadowOffsetY:1,textBoxShadowBlur:1,textBoxShadowOffsetX:1,textBoxShadowOffsetY:1},Ag=function(t,e,n){return Dg.hasOwnProperty(e)?n*=t.dpr:n},kg=[["shadowBlur",0],["shadowOffsetX",0],["shadowOffsetY",0],["shadowColor","#000"],["lineCap","butt"],["lineJoin","miter"],["miterLimit",10]],Pg=function(t,e){this.extendFrom(t,!1),this.host=e};Pg.prototype={constructor:Pg,host:null,fill:"#000",stroke:null,opacity:1,lineDash:null,lineDashOffset:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,lineWidth:1,strokeNoScale:!1,text:null,font:null,textFont:null,fontStyle:null,fontWeight:null,fontSize:null,fontFamily:null,textTag:null,textFill:"#000",textStroke:null,textWidth:null,textHeight:null,textStrokeWidth:0,textLineHeight:null,textPosition:"inside",textRect:null,textOffset:null,textAlign:null,textVerticalAlign:null,textDistance:5,textShadowColor:"transparent",textShadowBlur:0,textShadowOffsetX:0,textShadowOffsetY:0,textBoxShadowColor:"transparent",textBoxShadowBlur:0,textBoxShadowOffsetX:0,textBoxShadowOffsetY:0,transformText:!1,textRotation:0,textOrigin:null,textBackgroundColor:null,textBorderColor:null,textBorderWidth:0,textBorderRadius:0,textPadding:null,rich:null,truncate:null,blend:null,bind:function(t,e,n){for(var i=this,r=n&&n.style,o=!r,a=0;a0},extendFrom:function(t,e){if(t)for(var n in t)!t.hasOwnProperty(n)||!0!==e&&(!1===e?this.hasOwnProperty(n):null==t[n])||(this[n]=t[n])},set:function(t,e){"string"==typeof t?this[t]=e:this.extendFrom(t,!0)},clone:function(){var t=new this.constructor;return t.extendFrom(this,!0),t},getGradient:function(t,e,n){for(var i=("radial"===e.type?ie:ne)(t,e,n),r=e.colorStops,o=0;o=0&&n.splice(i,1),t.__hoverMir=null},clearHover:function(t){for(var e=this._hoverElements,n=0;n15)break}s.__drawIndex=m,s.__drawIndex0&&t>i[0]){for(a=0;at);a++);o=n[i[a]]}if(i.splice(a+1,0,t),n[t]=e,!e.virtual)if(o){var l=o.dom;l.nextSibling?s.insertBefore(e.dom,l.nextSibling):s.appendChild(e.dom)}else s.firstChild?s.insertBefore(e.dom,s.firstChild):s.appendChild(e.dom)}else yg("Layer of zlevel "+t+" is not valid")},eachLayer:function(t,e){var n,i,r=this._zlevelList;for(i=0;i0?.01:0),this._needsManuallyCompositing),o.__builtin__||yg("ZLevel "+s+" has been used by unkown layer "+o.id),o!==n&&(o.__used=!0,o.__startIndex!==r&&(o.__dirty=!0),o.__startIndex=r,o.incremental?o.__drawIndex=-1:o.__drawIndex=r,e(r),n=o),a.__dirty&&(o.__dirty=!0,o.incremental&&o.__drawIndex<0&&(o.__drawIndex=r))}e(r),this.eachBuiltinLayer(function(t,e){!t.__used&&t.getElementCount()>0&&(t.__dirty=!0,t.__startIndex=t.__endIndex=t.__drawIndex=0),t.__dirty&&t.__drawIndex<0&&(t.__drawIndex=t.__startIndex)})},clear:function(){return this.eachBuiltinLayer(this._clearLayer),this},_clearLayer:function(t){t.clear()},setBackgroundColor:function(t){this._backgroundColor=t},configLayer:function(t,e){if(e){var n=this._layerConfig;n[t]?i(n[t],e,!0):n[t]=e;for(var r=0;r=0&&this._clips.splice(e,1)},removeAnimator:function(t){for(var e=t.getClips(),n=0;n1&&i&&i.length>1){var o=ln(i)/ln(r);!isFinite(o)&&(o=1),e.pinchScale=o;var a=un(i);return e.pinchX=a[0],e.pinchY=a[1],{type:"pinch",target:t[0].target,event:e}}}}},rm=["click","dblclick","mousewheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],om=["touchstart","touchend","touchmove"],am={pointerdown:1,pointerup:1,pointermove:1,pointerout:1},sm=f(rm,function(t){var e=t.replace("mouse","pointer");return am[e]?e:t}),lm={mousemove:function(t){t=rn(this.dom,t),this.trigger("mousemove",t)},mouseout:function(t){var e=(t=rn(this.dom,t)).toElement||t.relatedTarget;if(e!=this.dom)for(;e&&9!=e.nodeType;){if(e===this.dom)return;e=e.parentNode}this.trigger("mouseout",t)},touchstart:function(t){(t=rn(this.dom,t)).zrByTouch=!0,this._lastTouchMoment=new Date,cn(this,t,"start"),lm.mousemove.call(this,t),lm.mousedown.call(this,t),dn(this)},touchmove:function(t){(t=rn(this.dom,t)).zrByTouch=!0,cn(this,t,"change"),lm.mousemove.call(this,t),dn(this)},touchend:function(t){(t=rn(this.dom,t)).zrByTouch=!0,cn(this,t,"end"),lm.mouseup.call(this,t),+new Date-this._lastTouchMoment<300&&lm.click.call(this,t),dn(this)},pointerdown:function(t){lm.mousedown.call(this,t)},pointermove:function(t){fn(t)||lm.mousemove.call(this,t)},pointerup:function(t){lm.mouseup.call(this,t)},pointerout:function(t){fn(t)||lm.mouseout.call(this,t)}};d(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],function(t){lm[t]=function(e){e=rn(this.dom,e),this.trigger(t,e)}});var um=gn.prototype;um.dispose=function(){for(var t=rm.concat(om),e=0;e=0||i&&l(i,a)<0)){var s=e.getShallow(a);null!=s&&(r[t[o][0]]=s)}}return r}},Im=Sm([["lineWidth","width"],["stroke","color"],["opacity"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["shadowColor"]]),Cm={getLineStyle:function(t){var e=Im(this,t),n=this.getLineDash(e.lineWidth);return n&&(e.lineDash=n),e},getLineDash:function(t){null==t&&(t=1);var e=this.get("type"),n=Math.max(t,2),i=4*t;return"solid"===e||null==e?null:"dashed"===e?[i,i]:[n,n]}},Tm=Sm([["fill","color"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["opacity"],["shadowColor"]]),Dm={getAreaStyle:function(t,e){return Tm(this,t,e)}},Am=Math.pow,km=Math.sqrt,Pm=1e-8,Lm=1e-4,Om=km(3),zm=1/3,Em=B(),Nm=B(),Rm=B(),Bm=Math.min,Vm=Math.max,Fm=Math.sin,Hm=Math.cos,Gm=2*Math.PI,Wm=B(),Zm=B(),Um=B(),Xm=[],jm=[],Ym={M:1,L:2,C:3,Q:4,A:5,Z:6,R:7},qm=[],$m=[],Km=[],Qm=[],Jm=Math.min,tv=Math.max,ev=Math.cos,nv=Math.sin,iv=Math.sqrt,rv=Math.abs,ov="undefined"!=typeof Float32Array,av=function(t){this._saveData=!t,this._saveData&&(this.data=[]),this._ctx=null};av.prototype={constructor:av,_xi:0,_yi:0,_x0:0,_y0:0,_ux:0,_uy:0,_len:0,_lineDash:null,_dashOffset:0,_dashIdx:0,_dashSum:0,setScale:function(t,e){this._ux=rv(1/mg/t)||0,this._uy=rv(1/mg/e)||0},getContext:function(){return this._ctx},beginPath:function(t){return this._ctx=t,t&&t.beginPath(),t&&(this.dpr=t.dpr),this._saveData&&(this._len=0),this._lineDash&&(this._lineDash=null,this._dashOffset=0),this},moveTo:function(t,e){return this.addData(Ym.M,t,e),this._ctx&&this._ctx.moveTo(t,e),this._x0=t,this._y0=e,this._xi=t,this._yi=e,this},lineTo:function(t,e){var n=rv(t-this._xi)>this._ux||rv(e-this._yi)>this._uy||this._len<5;return this.addData(Ym.L,t,e),this._ctx&&n&&(this._needsDash()?this._dashedLineTo(t,e):this._ctx.lineTo(t,e)),n&&(this._xi=t,this._yi=e),this},bezierCurveTo:function(t,e,n,i,r,o){return this.addData(Ym.C,t,e,n,i,r,o),this._ctx&&(this._needsDash()?this._dashedBezierTo(t,e,n,i,r,o):this._ctx.bezierCurveTo(t,e,n,i,r,o)),this._xi=r,this._yi=o,this},quadraticCurveTo:function(t,e,n,i){return this.addData(Ym.Q,t,e,n,i),this._ctx&&(this._needsDash()?this._dashedQuadraticTo(t,e,n,i):this._ctx.quadraticCurveTo(t,e,n,i)),this._xi=n,this._yi=i,this},arc:function(t,e,n,i,r,o){return this.addData(Ym.A,t,e,n,n,i,r-i,0,o?0:1),this._ctx&&this._ctx.arc(t,e,n,i,r,o),this._xi=ev(r)*n+t,this._yi=nv(r)*n+t,this},arcTo:function(t,e,n,i,r){return this._ctx&&this._ctx.arcTo(t,e,n,i,r),this},rect:function(t,e,n,i){return this._ctx&&this._ctx.rect(t,e,n,i),this.addData(Ym.R,t,e,n,i),this},closePath:function(){this.addData(Ym.Z);var t=this._ctx,e=this._x0,n=this._y0;return t&&(this._needsDash()&&this._dashedLineTo(e,n),t.closePath()),this._xi=e,this._yi=n,this},fill:function(t){t&&t.fill(),this.toStatic()},stroke:function(t){t&&t.stroke(),this.toStatic()},setLineDash:function(t){if(t instanceof Array){this._lineDash=t,this._dashIdx=0;for(var e=0,n=0;ne.length&&(this._expandData(),e=this.data);for(var n=0;n0&&f<=t||h<0&&f>=t||0==h&&(c>0&&p<=e||c<0&&p>=e);)f+=h*(n=a[i=this._dashIdx]),p+=c*n,this._dashIdx=(i+1)%g,h>0&&fl||c>0&&pu||s[i%2?"moveTo":"lineTo"](h>=0?Jm(f,t):tv(f,t),c>=0?Jm(p,e):tv(p,e));h=f-t,c=p-e,this._dashOffset=-iv(h*h+c*c)},_dashedBezierTo:function(t,e,n,i,r,o){var a,s,l,u,h,c=this._dashSum,d=this._dashOffset,f=this._lineDash,p=this._ctx,g=this._xi,m=this._yi,v=Gn,y=0,x=this._dashIdx,_=f.length,w=0;for(d<0&&(d=c+d),d%=c,a=0;a<1;a+=.1)s=v(g,t,n,r,a+.1)-v(g,t,n,r,a),l=v(m,e,i,o,a+.1)-v(m,e,i,o,a),y+=iv(s*s+l*l);for(;x<_&&!((w+=f[x])>d);x++);for(a=(w-d)/y;a<=1;)u=v(g,t,n,r,a),h=v(m,e,i,o,a),x%2?p.moveTo(u,h):p.lineTo(u,h),a+=f[x]/y,x=(x+1)%_;x%2!=0&&p.lineTo(r,o),s=r-u,l=o-h,this._dashOffset=-iv(s*s+l*l)},_dashedQuadraticTo:function(t,e,n,i){var r=n,o=i;n=(n+2*t)/3,i=(i+2*e)/3,t=(this._xi+2*t)/3,e=(this._yi+2*e)/3,this._dashedBezierTo(t,e,n,i,r,o)},toStatic:function(){var t=this.data;t instanceof Array&&(t.length=this._len,ov&&(this.data=new Float32Array(t)))},getBoundingRect:function(){qm[0]=qm[1]=Km[0]=Km[1]=Number.MAX_VALUE,$m[0]=$m[1]=Qm[0]=Qm[1]=-Number.MAX_VALUE;for(var t=this.data,e=0,n=0,i=0,r=0,o=0;ol||rv(a-r)>u||c===h-1)&&(t.lineTo(o,a),i=o,r=a);break;case Ym.C:t.bezierCurveTo(s[c++],s[c++],s[c++],s[c++],s[c++],s[c++]),i=s[c-2],r=s[c-1];break;case Ym.Q:t.quadraticCurveTo(s[c++],s[c++],s[c++],s[c++]),i=s[c-2],r=s[c-1];break;case Ym.A:var f=s[c++],p=s[c++],g=s[c++],m=s[c++],v=s[c++],y=s[c++],x=s[c++],_=s[c++],w=g>m?g:m,b=g>m?1:g/m,M=g>m?m/g:1,S=v+y;Math.abs(g-m)>.001?(t.translate(f,p),t.rotate(x),t.scale(b,M),t.arc(0,0,w,v,S,1-_),t.scale(1/b,1/M),t.rotate(-x),t.translate(-f,-p)):t.arc(f,p,w,v,S,1-_),1==c&&(e=ev(v)*g+f,n=nv(v)*m+p),i=ev(S)*g+f,r=nv(S)*m+p;break;case Ym.R:e=i=s[c],n=r=s[c+1],t.rect(s[c++],s[c++],s[c++],s[c++]);break;case Ym.Z:t.closePath(),i=e,r=n}}}},av.CMD=Ym;var sv=2*Math.PI,lv=2*Math.PI,uv=av.CMD,hv=2*Math.PI,cv=1e-4,dv=[-1,-1,-1],fv=[-1,-1],pv=Eg.prototype.getCanvasPattern,gv=Math.abs,mv=new av(!0);xi.prototype={constructor:xi,type:"path",__dirtyPath:!0,strokeContainThreshold:5,brush:function(t,e){var n=this.style,i=this.path||mv,r=n.hasStroke(),o=n.hasFill(),a=n.fill,s=n.stroke,l=o&&!!a.colorStops,u=r&&!!s.colorStops,h=o&&!!a.image,c=r&&!!s.image;if(n.bind(t,this,e),this.setTransform(t),this.__dirty){var d;l&&(d=d||this.getBoundingRect(),this._fillGradient=n.getGradient(t,a,d)),u&&(d=d||this.getBoundingRect(),this._strokeGradient=n.getGradient(t,s,d))}l?t.fillStyle=this._fillGradient:h&&(t.fillStyle=pv.call(a,t)),u?t.strokeStyle=this._strokeGradient:c&&(t.strokeStyle=pv.call(s,t));var f=n.lineDash,p=n.lineDashOffset,g=!!t.setLineDash,m=this.getGlobalScale();i.setScale(m[0],m[1]),this.__dirtyPath||f&&!g&&r?(i.beginPath(t),f&&!g&&(i.setLineDash(f),i.setLineDashOffset(p)),this.buildPath(i,this.shape,!1),this.path&&(this.__dirtyPath=!1)):(t.beginPath(),this.path.rebuildPath(t)),o&&i.fill(t),f&&g&&(t.setLineDash(f),t.lineDashOffset=p),r&&i.stroke(t),f&&g&&t.setLineDash([]),null!=n.text&&(this.restoreTransform(t),this.drawRectText(t,this.getBoundingRect()))},buildPath:function(t,e,n){},createPathProxy:function(){this.path=new av},getBoundingRect:function(){var t=this._rect,e=this.style,n=!t;if(n){var i=this.path;i||(i=this.path=new av),this.__dirtyPath&&(i.beginPath(),this.buildPath(i,this.shape,!1)),t=i.getBoundingRect()}if(this._rect=t,e.hasStroke()){var r=this._rectWithStroke||(this._rectWithStroke=t.clone());if(this.__dirty||n){r.copy(t);var o=e.lineWidth,a=e.strokeNoScale?this.getLineScale():1;e.hasFill()||(o=Math.max(o,this.strokeContainThreshold||4)),a>1e-10&&(r.width+=o/a,r.height+=o/a,r.x-=o/a/2,r.y-=o/a/2)}return r}return t},contain:function(t,e){var n=this.transformCoordToLocal(t,e),i=this.getBoundingRect(),r=this.style;if(t=n[0],e=n[1],i.contain(t,e)){var o=this.path.data;if(r.hasStroke()){var a=r.lineWidth,s=r.strokeNoScale?this.getLineScale():1;if(s>1e-10&&(r.hasFill()||(a=Math.max(a,this.strokeContainThreshold)),yi(o,a/s,t,e)))return!0}if(r.hasFill())return vi(o,t,e)}return!1},dirty:function(t){null==t&&(t=!0),t&&(this.__dirtyPath=t,this._rect=null),this.__dirty=!0,this.__zr&&this.__zr.refresh(),this.__clipTarget&&this.__clipTarget.dirty()},animateShape:function(t){return this.animate("shape",t)},attrKV:function(t,e){"shape"===t?(this.setShape(e),this.__dirtyPath=!0,this._rect=null):Xe.prototype.attrKV.call(this,t,e)},setShape:function(t,e){var n=this.shape;if(n){if(w(t))for(var i in t)t.hasOwnProperty(i)&&(n[i]=t[i]);else n[t]=e;this.dirty(!0)}return this},getLineScale:function(){var t=this.transform;return t&&gv(t[0]-1)>1e-10&&gv(t[3]-1)>1e-10?Math.sqrt(gv(t[0]*t[3]-t[2]*t[1])):1}},xi.extend=function(t){var e=function(e){xi.call(this,e),t.style&&this.style.extendFrom(t.style,!1);var n=t.shape;if(n){this.shape=this.shape||{};var i=this.shape;for(var r in n)!i.hasOwnProperty(r)&&n.hasOwnProperty(r)&&(i[r]=n[r])}t.init&&t.init.call(this,e)};u(e,xi);for(var n in t)"style"!==n&&"shape"!==n&&(e.prototype[n]=t[n]);return e},u(xi,Xe);var vv=av.CMD,yv=[[],[],[]],xv=Math.sqrt,_v=Math.atan2,wv=function(t,e){var n,i,r,o,a,s,l=t.data,u=vv.M,h=vv.C,c=vv.L,d=vv.R,f=vv.A,p=vv.Q;for(r=0,o=0;r=11?function(){var e,n=this.__clipPaths,i=this.style;if(n)for(var r=0;rn-2?n-1:c+1],u=t[c>n-3?n-1:c+2]);var p=d*d,g=d*p;i.push([Ii(s[0],f[0],l[0],u[0],d,p,g),Ii(s[1],f[1],l[1],u[1],d,p,g)])}return i},Rv=function(t,e,n,i){var r,o,a,s,l=[],u=[],h=[],c=[];if(i){a=[1/0,1/0],s=[-1/0,-1/0];for(var d=0,f=t.length;d=n&&o>=r)return{x:n,y:r,width:i-n,height:o-r}},createIcon:fr,Group:Sg,Image:je,Text:kv,Circle:Pv,Sector:zv,Ring:Ev,Polygon:Bv,Polyline:Vv,Rect:Fv,Line:Hv,BezierCurve:Wv,Arc:Zv,IncrementalDisplayable:Di,CompoundPath:Uv,LinearGradient:jv,RadialGradient:Yv,BoundingRect:Xt}),ey=["textStyle","color"],ny={getTextColor:function(t){var e=this.ecModel;return this.getShallow("color")||(!t&&e?e.get(ey):null)},getFont:function(){return rr({fontStyle:this.getShallow("fontStyle"),fontWeight:this.getShallow("fontWeight"),fontSize:this.getShallow("fontSize"),fontFamily:this.getShallow("fontFamily")},this.ecModel)},getTextRect:function(t){return ce(t,this.getFont(),this.getShallow("align"),this.getShallow("verticalAlign")||this.getShallow("baseline"),this.getShallow("padding"),this.getShallow("rich"),this.getShallow("truncateText"))}},iy=Sm([["fill","color"],["stroke","borderColor"],["lineWidth","borderWidth"],["opacity"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["shadowColor"],["textPosition"],["textAlign"]]),ry={getItemStyle:function(t,e){var n=iy(this,t,e),i=this.getBorderLineDash();return i&&(n.lineDash=i),n},getBorderLineDash:function(){var t=this.get("borderType");return"solid"===t||null==t?null:"dashed"===t?[5,5]:[1,1]}},oy=h,ay=Dn();pr.prototype={constructor:pr,init:null,mergeOption:function(t){i(this.option,t,!0)},get:function(t,e){return null==t?this.option:gr(this.option,this.parsePath(t),!e&&mr(this,t))},getShallow:function(t,e){var n=this.option,i=null==n?n:n[t],r=!e&&mr(this,t);return null==i&&r&&(i=r.getShallow(t)),i},getModel:function(t,e){var n,i=null==t?this.option:gr(this.option,t=this.parsePath(t));return e=e||(n=mr(this,t))&&n.getModel(t),new pr(i,e,this.ecModel)},isEmpty:function(){return null==this.option},restoreData:function(){},clone:function(){return new(0,this.constructor)(n(this.option))},setReadOnly:function(t){},parsePath:function(t){return"string"==typeof t&&(t=t.split(".")),t},customizeGetParent:function(t){ay(this).getParent=t},isAnimationEnabled:function(){if(!bp.node){if(null!=this.option.animation)return!!this.option.animation;if(this.parentModel)return this.parentModel.isAnimationEnabled()}}},En(pr),Nn(pr),oy(pr,Cm),oy(pr,Dm),oy(pr,ny),oy(pr,ry);var sy=0,ly=1e-4,uy=/^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/,hy=(Object.freeze||Object)({linearMap:xr,parsePercent:_r,round:wr,asc:br,getPrecision:Mr,getPrecisionSafe:Sr,getPixelPrecision:Ir,getPercentWithPrecision:Cr,MAX_SAFE_INTEGER:9007199254740991,remRadian:Tr,isRadianAroundZero:Dr,parseDate:Ar,quantity:kr,nice:Lr,quantile:function(t,e){var n=(t.length-1)*e+1,i=Math.floor(n),r=+t[i-1],o=n-i;return o?r+o*(t[i]-r):r},reformIntervals:function(t){function e(t,n,i){return t.interval[i]=0}}),cy=k,dy=/([&<>"'])/g,fy={"&":"&","<":"<",">":">",'"':""","'":"'"},py=["a","b","c","d","e","f","g"],gy=function(t,e){return"{"+t+(null==e?"":e)+"}"},my=ve,vy=ce,yy=(Object.freeze||Object)({addCommas:Or,toCamelCase:zr,normalizeCssArray:cy,encodeHTML:Er,formatTpl:Nr,formatTplSimple:function(t,e,n){return d(e,function(e,i){t=t.replace("{"+i+"}",n?Er(e):e)}),t},getTooltipMarker:Rr,formatTime:Vr,capitalFirst:Fr,truncateText:my,getTextRect:vy}),xy=d,_y=["left","right","top","bottom","width","height"],wy=[["width","left","right"],["height","top","bottom"]],by=Hr,My=(v(Hr,"vertical"),v(Hr,"horizontal"),{getBoxLayoutParams:function(){return{left:this.get("left"),top:this.get("top"),right:this.get("right"),bottom:this.get("bottom"),width:this.get("width"),height:this.get("height")}}}),Sy=Dn(),Iy=pr.extend({type:"component",id:"",name:"",mainType:"",subType:"",componentIndex:0,defaultOption:null,ecModel:null,dependentModels:[],uid:null,layoutMode:null,$constructor:function(t,e,n,i){pr.call(this,t,e,n,i),this.uid=vr("ec_cpt_model")},init:function(t,e,n,i){this.mergeDefaultAndTheme(t,n)},mergeDefaultAndTheme:function(t,e){var n=this.layoutMode,r=n?Ur(t):{};i(t,e.getTheme().get(this.mainType)),i(t,this.getDefaultOption()),n&&Zr(t,r,n)},mergeOption:function(t,e){i(this.option,t,!0);var n=this.layoutMode;n&&Zr(this.option,t,n)},optionUpdated:function(t,e){},getDefaultOption:function(){var t=Sy(this);if(!t.defaultOption){for(var e=[],n=this.constructor;n;){var r=n.prototype.defaultOption;r&&e.push(r),n=n.superClass}for(var o={},a=e.length-1;a>=0;a--)o=i(o,e[a],!0);t.defaultOption=o}return t.defaultOption},getReferringComponents:function(t){return this.ecModel.queryComponents({mainType:t,index:this.get(t+"Index",!0),id:this.get(t+"Id",!0)})}});Vn(Iy,{registerWhenExtend:!0}),function(t){var e={};t.registerSubTypeDefaulter=function(t,n){t=On(t),e[t.main]=n},t.determineSubType=function(n,i){var r=i.type;if(!r){var o=On(n).main;t.hasSubTypes(n)&&e[o]&&(r=e[o](i))}return r}}(Iy),function(t,e){function n(t){var n={},o=[];return d(t,function(a){var s=i(n,a),u=r(s.originalDeps=e(a),t);s.entryCount=u.length,0===s.entryCount&&o.push(a),d(u,function(t){l(s.predecessor,t)<0&&s.predecessor.push(t);var e=i(n,t);l(e.successor,t)<0&&e.successor.push(a)})}),{graph:n,noEntryList:o}}function i(t,e){return t[e]||(t[e]={predecessor:[],successor:[]}),t[e]}function r(t,e){var n=[];return d(t,function(t){l(e,t)>=0&&n.push(t)}),n}t.topologicalTravel=function(t,e,i,r){function o(t){s[t].entryCount--,0===s[t].entryCount&&l.push(t)}if(t.length){var a=n(e),s=a.graph,l=a.noEntryList,u={};for(d(t,function(t){u[t]=!0});l.length;){var h=l.pop(),c=s[h],f=!!u[h];f&&(i.call(r,h,c.originalDeps.slice()),delete u[h]),d(c.successor,f?function(t){u[t]=!0,o(t)}:o)}d(u,function(){throw new Error("Circle dependency may exists")})}}}(Iy,function(t){var e=[];return d(Iy.getClassesByMainType(t),function(t){e=e.concat(t.prototype.dependencies||[])}),e=f(e,function(t){return On(t).main}),"dataset"!==t&&l(e,"dataset")<=0&&e.unshift("dataset"),e}),h(Iy,My);var Cy="";"undefined"!=typeof navigator&&(Cy=navigator.platform||"");var Ty={color:["#c23531","#2f4554","#61a0a8","#d48265","#91c7ae","#749f83","#ca8622","#bda29a","#6e7074","#546570","#c4ccd3"],gradientColor:["#f6efa6","#d88273","#bf444c"],textStyle:{fontFamily:Cy.match(/^Win/)?"Microsoft YaHei":"sans-serif",fontSize:12,fontStyle:"normal",fontWeight:"normal"},blendMode:null,animation:"auto",animationDuration:1e3,animationDurationUpdate:300,animationEasing:"exponentialOut",animationEasingUpdate:"cubicOut",animationThreshold:2e3,progressiveThreshold:3e3,progressive:400,hoverLayerThreshold:3e3,useUTC:!1},Dy=Dn(),Ay={clearColorPalette:function(){Dy(this).colorIdx=0,Dy(this).colorNameMap={}},getColorFromPalette:function(t,e,n){var i=Dy(e=e||this),r=i.colorIdx||0,o=i.colorNameMap=i.colorNameMap||{};if(o.hasOwnProperty(t))return o[t];var a=xn(this.get("color",!0)),s=this.get("colorLayer",!0),l=null!=n&&s?jr(s,n):a;if((l=l||a)&&l.length){var u=l[r];return t&&(o[t]=u),i.colorIdx=(r+1)%l.length,u}}},ky={cartesian2d:function(t,e,n,i){var r=t.getReferringComponents("xAxis")[0],o=t.getReferringComponents("yAxis")[0];e.coordSysDims=["x","y"],n.set("x",r),n.set("y",o),qr(r)&&(i.set("x",r),e.firstCategoryDimIndex=0),qr(o)&&(i.set("y",o),e.firstCategoryDimIndex=1)},singleAxis:function(t,e,n,i){var r=t.getReferringComponents("singleAxis")[0];e.coordSysDims=["single"],n.set("single",r),qr(r)&&(i.set("single",r),e.firstCategoryDimIndex=0)},polar:function(t,e,n,i){var r=t.getReferringComponents("polar")[0],o=r.findAxisModel("radiusAxis"),a=r.findAxisModel("angleAxis");e.coordSysDims=["radius","angle"],n.set("radius",o),n.set("angle",a),qr(o)&&(i.set("radius",o),e.firstCategoryDimIndex=0),qr(a)&&(i.set("angle",a),e.firstCategoryDimIndex=1)},geo:function(t,e,n,i){e.coordSysDims=["lng","lat"]},parallel:function(t,e,n,i){var r=t.ecModel,o=r.getComponent("parallel",t.get("parallelIndex")),a=e.coordSysDims=o.dimensions.slice();d(o.parallelAxisIndex,function(t,o){var s=r.getComponent("parallelAxis",t),l=a[o];n.set(l,s),qr(s)&&null==e.firstCategoryDimIndex&&(i.set(l,s),e.firstCategoryDimIndex=o)})}},Py="original",Ly="arrayRows",Oy="objectRows",zy="keyedColumns",Ey="unknown",Ny="typedArray",Ry="column",By="row";$r.seriesDataToSource=function(t){return new $r({data:t,sourceFormat:M(t)?Ny:Py,fromDataset:!1})},Nn($r);var Vy=Dn(),Fy="\0_ec_inner",Hy=pr.extend({init:function(t,e,n,i){n=n||{},this.option=null,this._theme=new pr(n),this._optionManager=i},setOption:function(t,e){P(!(Fy in t),"please use chart.getOption()"),this._optionManager.setOption(t,e),this.resetOption(null)},resetOption:function(t){var e=!1,n=this._optionManager;if(!t||"recreate"===t){var i=n.mountOption("recreate"===t);this.option&&"recreate"!==t?(this.restoreData(),this.mergeOption(i)):co.call(this,i),e=!0}if("timeline"!==t&&"media"!==t||this.restoreData(),!t||"recreate"===t||"timeline"===t){var r=n.getTimelineOption(this);r&&(this.mergeOption(r),e=!0)}if(!t||"recreate"===t||"media"===t){var o=n.getMediaOption(this,this._api);o.length&&d(o,function(t){this.mergeOption(t,e=!0)},this)}return e},mergeOption:function(t){var e=this.option,r=this._componentsMap,a=[];Jr(this),d(t,function(t,r){null!=t&&(Iy.hasClass(r)?r&&a.push(r):e[r]=null==e[r]?n(t):i(e[r],t,!0))}),Iy.topologicalTravel(a,Iy.getAllClassMainTypes(),function(n,i){var a=xn(t[n]),s=Mn(r.get(n),a);Sn(s),d(s,function(t,e){var i=t.option;w(i)&&(t.keyInfo.mainType=n,t.keyInfo.subType=po(n,i,t.exist))});var l=fo(r,i);e[n]=[],r.set(n,[]),d(s,function(t,i){var a=t.exist,s=t.option;if(P(w(s)||a,"Empty component definition"),s){var u=Iy.getClass(n,t.keyInfo.subType,!0);if(a&&a instanceof u)a.name=t.keyInfo.name,a.mergeOption(s,this),a.optionUpdated(s,!1);else{var h=o({dependentModels:l,componentIndex:i},t.keyInfo);o(a=new u(s,this,this,h),h),a.init(s,this,this,h),a.optionUpdated(null,!0)}}else a.mergeOption({},this),a.optionUpdated({},!1);r.get(n)[i]=a,e[n][i]=a.option},this),"series"===n&&go(this,r.get("series"))},this),this._seriesIndicesMap=N(this._seriesIndices=this._seriesIndices||[])},getOption:function(){var t=n(this.option);return d(t,function(e,n){if(Iy.hasClass(n)){for(var i=(e=xn(e)).length-1;i>=0;i--)Cn(e[i])&&e.splice(i,1);t[n]=e}}),delete t[Fy],t},getTheme:function(){return this._theme},getComponent:function(t,e){var n=this._componentsMap.get(t);if(n)return n[e||0]},queryComponents:function(t){var e=t.mainType;if(!e)return[];var n=t.index,i=t.id,r=t.name,o=this._componentsMap.get(e);if(!o||!o.length)return[];var a;if(null!=n)y(n)||(n=[n]),a=g(f(n,function(t){return o[t]}),function(t){return!!t});else if(null!=i){var s=y(i);a=g(o,function(t){return s&&l(i,t.id)>=0||!s&&t.id===i})}else if(null!=r){var u=y(r);a=g(o,function(t){return u&&l(r,t.name)>=0||!u&&t.name===r})}else a=o.slice();return mo(a,t)},findComponents:function(t){var e=t.query,n=t.mainType,i=function(t){var e=n+"Index",i=n+"Id",r=n+"Name";return!t||null==t[e]&&null==t[i]&&null==t[r]?null:{mainType:n,index:t[e],id:t[i],name:t[r]}}(e);return function(e){return t.filter?g(e,t.filter):e}(mo(i?this.queryComponents(i):this._componentsMap.get(n),t))},eachComponent:function(t,e,n){var i=this._componentsMap;"function"==typeof t?(n=e,e=t,i.each(function(t,i){d(t,function(t,r){e.call(n,i,t,r)})})):_(t)?d(i.get(t),e,n):w(t)&&d(this.findComponents(t),e,n)},getSeriesByName:function(t){return g(this._componentsMap.get("series"),function(e){return e.name===t})},getSeriesByIndex:function(t){return this._componentsMap.get("series")[t]},getSeriesByType:function(t){return g(this._componentsMap.get("series"),function(e){return e.subType===t})},getSeries:function(){return this._componentsMap.get("series").slice()},getSeriesCount:function(){return this._componentsMap.get("series").length},eachSeries:function(t,e){d(this._seriesIndices,function(n){var i=this._componentsMap.get("series")[n];t.call(e,i,n)},this)},eachRawSeries:function(t,e){d(this._componentsMap.get("series"),t,e)},eachSeriesByType:function(t,e,n){d(this._seriesIndices,function(i){var r=this._componentsMap.get("series")[i];r.subType===t&&e.call(n,r,i)},this)},eachRawSeriesByType:function(t,e,n){return d(this.getSeriesByType(t),e,n)},isSeriesFiltered:function(t){return null==this._seriesIndicesMap.get(t.componentIndex)},getCurrentSeriesIndices:function(){return(this._seriesIndices||[]).slice()},filterSeries:function(t,e){go(this,g(this._componentsMap.get("series"),t,e))},restoreData:function(t){var e=this._componentsMap;go(this,e.get("series"));var n=[];e.each(function(t,e){n.push(e)}),Iy.topologicalTravel(n,Iy.getAllClassMainTypes(),function(n,i){d(e.get(n),function(e){("series"!==n||!uo(e,t))&&e.restoreData()})})}});h(Hy,Ay);var Gy=["getDom","getZr","getWidth","getHeight","getDevicePixelRatio","dispatchAction","isDisposed","on","off","getDataURL","getConnectedDataURL","getModel","getOption","getViewOfComponentModel","getViewOfSeriesModel"],Wy={};yo.prototype={constructor:yo,create:function(t,e){var n=[];d(Wy,function(i,r){var o=i.create(t,e);n=n.concat(o||[])}),this._coordinateSystems=n},update:function(t,e){d(this._coordinateSystems,function(n){n.update&&n.update(t,e)})},getCoordinateSystems:function(){return this._coordinateSystems.slice()}},yo.register=function(t,e){Wy[t]=e},yo.get=function(t){return Wy[t]};var Zy=d,Uy=n,Xy=f,jy=i,Yy=/^(min|max)?(.+)$/;xo.prototype={constructor:xo,setOption:function(t,e){t&&d(xn(t.series),function(t){t&&t.data&&M(t.data)&&O(t.data)}),t=Uy(t,!0);var n=this._optionBackup,i=_o.call(this,t,e,!n);this._newBaseOption=i.baseOption,n?(So(n.baseOption,i.baseOption),i.timelineOptions.length&&(n.timelineOptions=i.timelineOptions),i.mediaList.length&&(n.mediaList=i.mediaList),i.mediaDefault&&(n.mediaDefault=i.mediaDefault)):this._optionBackup=i},mountOption:function(t){var e=this._optionBackup;return this._timelineOptions=Xy(e.timelineOptions,Uy),this._mediaList=Xy(e.mediaList,Uy),this._mediaDefault=Uy(e.mediaDefault),this._currentMediaIndices=[],Uy(t?e.baseOption:this._newBaseOption)},getTimelineOption:function(t){var e,n=this._timelineOptions;if(n.length){var i=t.getComponent("timeline");i&&(e=Uy(n[i.getCurrentIndex()],!0))}return e},getMediaOption:function(t){var e=this._api.getWidth(),n=this._api.getHeight(),i=this._mediaList,r=this._mediaDefault,o=[],a=[];if(!i.length&&!r)return a;for(var s=0,l=i.length;s=1)&&(t=1),t}var n=this._upstream,i=t&&t.skip;if(this._dirty&&n){var r=this.context;r.data=r.outputData=n.context.outputData}this.__pipeline&&(this.__pipeline.currentTask=this);var o;this._plan&&!i&&(o=this._plan(this.context));var a=e(this._modBy),s=this._modDataCount||0,l=e(t&&t.modBy),u=t&&t.modDataCount||0;a===l&&s===u||(o="reset");var h;(this._dirty||"reset"===o)&&(this._dirty=!1,h=qo(this,i)),this._modBy=l,this._modDataCount=u;var c=t&&t.step;if(this._dueEnd=n?n._outputDueEnd:this._count?this._count(this.context):1/0,this._progress){var d=this._dueIndex,f=Math.min(null!=c?this._dueIndex+c:1/0,this._dueEnd);if(!i&&(h||d=n?null:t1&&o>0?e:t}};return s}();lx.dirty=function(){this._dirty=!0,this._onDirty&&this._onDirty(this.context)},lx.unfinished=function(){return this._progress&&this._dueIndex1||l&&!a?function(n){function i(t,n){var i=r.getDimensionInfo(n);if(i&&!1!==i.otherDims.tooltip){var o=i.type,l=Rr({color:u,type:"subItem"}),h=(a?l+Er(i.displayName||"-")+": ":"")+Er("ordinal"===o?t+"":"time"===o?e?"":Vr("yyyy/MM/dd hh:mm:ss",t):Or(t));h&&s.push(h)}}var a=p(n,function(t,e,n){var i=r.getDimensionInfo(n);return t|=i&&!1!==i.tooltip&&null!=i.displayName},0),s=[];return o.length?d(o,function(e){i(Zo(r,t,e),e)}):d(n,i),(a?"
":"")+s.join(a?"
":", ")}(s):i(a?Zo(r,t,o[0]):l?s[0]:s),c=Rr(u),f=r.getName(t),g=this.name;return In(this)||(g=""),g=g?Er(g)+(e?": ":"
"):"",e?c+g+h:g+c+(f?Er(f)+": "+h:h)},isAnimationEnabled:function(){if(bp.node)return!1;var t=this.getShallow("animation");return t&&this.getData().count()>this.getShallow("animationThreshold")&&(t=!1),t},restoreData:function(){this.dataTask.dirty()},getColorFromPalette:function(t,e,n){var i=this.ecModel,r=Ay.getColorFromPalette.call(this,t,e,n);return r||(r=i.getColorFromPalette(t,e,n)),r},coordDimToDataDim:function(t){return this.getRawData().mapDimension(t,!0)},getProgressive:function(){return this.get("progressive")},getProgressiveThreshold:function(){return this.get("progressiveThreshold")},getAxisTooltipData:null,getTooltipPosition:null,pipeTask:null,preventIncremental:null,pipelineContext:null});h(cx,sx),h(cx,Ay);var dx=function(){this.group=new Sg,this.uid=vr("viewComponent")};dx.prototype={constructor:dx,init:function(t,e){},render:function(t,e,n,i){},dispose:function(){}};var fx=dx.prototype;fx.updateView=fx.updateLayout=fx.updateVisual=function(t,e,n,i){},En(dx),Vn(dx,{registerWhenExtend:!0});var px=function(){var t=Dn();return function(e){var n=t(e),i=e.pipelineContext,r=n.large,o=n.progressiveRender,a=n.large=i.large,s=n.progressiveRender=i.progressiveRender;return!!(r^a||o^s)&&"reset"}},gx=Dn(),mx=px();ra.prototype={type:"chart",init:function(t,e){},render:function(t,e,n,i){},highlight:function(t,e,n,i){aa(t.getData(),i,"emphasis")},downplay:function(t,e,n,i){aa(t.getData(),i,"normal")},remove:function(t,e){this.group.removeAll()},dispose:function(){},incrementalPrepareRender:null,incrementalRender:null,updateTransform:null};var vx=ra.prototype;vx.updateView=vx.updateLayout=vx.updateVisual=function(t,e,n,i){this.render(t,e,n,i)},En(ra),Vn(ra,{registerWhenExtend:!0}),ra.markUpdateMethod=function(t,e){gx(t).updateMethod=e};var yx={incrementalPrepareRender:{progress:function(t,e){e.view.incrementalRender(t,e.model,e.ecModel,e.api,e.payload)}},render:{forceFirstProgress:!0,progress:function(t,e){e.view.render(e.model,e.ecModel,e.api,e.payload)}}},xx="\0__throttleOriginMethod",_x="\0__throttleRate",bx="\0__throttleType",Mx={createOnAllSeries:!0,performRawSeries:!0,reset:function(t,e){var n=t.getData(),i=(t.visualColorAccessPath||"itemStyle.color").split("."),r=t.get(i)||t.getColorFromPalette(t.name,null,e.getSeriesCount());if(n.setVisual("color",r),!e.isSeriesFiltered(t)){"function"!=typeof r||r instanceof Xv||n.each(function(e){n.setItemVisual(e,"color",r(t.getDataParams(e)))});return{dataEach:n.hasItemOption?function(t,e){var n=t.getItemModel(e).get(i,!0);null!=n&&t.setItemVisual(e,"color",n)}:null}}}},Sx={toolbox:{brush:{title:{rect:"矩形选择",polygon:"圈选",lineX:"横向选择",lineY:"纵向选择",keep:"保持选择",clear:"清除选择"}},dataView:{title:"数据视图",lang:["数据视图","关闭","刷新"]},dataZoom:{title:{zoom:"区域缩放",back:"区域缩放还原"}},magicType:{title:{line:"切换为折线图",bar:"切换为柱状图",stack:"切换为堆叠",tiled:"切换为平铺"}},restore:{title:"还原"},saveAsImage:{title:"保存为图片",lang:["右键另存为图片"]}},series:{typeNames:{pie:"饼图",bar:"柱状图",line:"折线图",scatter:"散点图",effectScatter:"涟漪散点图",radar:"雷达图",tree:"树图",treemap:"矩形树图",boxplot:"箱型图",candlestick:"K线图",k:"K线图",heatmap:"热力图",map:"地图",parallel:"平行坐标图",lines:"线图",graph:"关系图",sankey:"桑基图",funnel:"漏斗图",gauge:"仪表盘图",pictorialBar:"象形柱图",themeRiver:"主题河流图",sunburst:"旭日图"}},aria:{general:{withTitle:"这是一个关于“{title}”的图表。",withoutTitle:"这是一个图表,"},series:{single:{prefix:"",withName:"图表类型是{seriesType},表示{seriesName}。",withoutName:"图表类型是{seriesType}。"},multiple:{prefix:"它由{seriesCount}个图表系列组成。",withName:"第{seriesId}个系列是一个表示{seriesName}的{seriesType},",withoutName:"第{seriesId}个系列是一个{seriesType},",separator:{middle:";",end:"。"}}},data:{allData:"其数据是——",partialData:"其中,前{displayCnt}项是——",withName:"{name}的数据是{value}",withoutName:"{value}",separator:{middle:",",end:""}}}},Ix=function(t,e){function n(t,e){if("string"!=typeof t)return t;var n=t;return d(e,function(t,e){n=n.replace(new RegExp("\\{\\s*"+e+"\\s*\\}","g"),t)}),n}function i(t){var e=o.get(t);if(null==e){for(var n=t.split("."),i=Sx.aria,r=0;r1?"series.multiple.prefix":"series.single.prefix"),{seriesCount:a}),e.eachSeries(function(t,e){if(e1?"multiple":"single")+".";o=n(o=i(s?u+"withName":u+"withoutName"),{seriesId:t.seriesIndex,seriesName:t.get("name"),seriesType:r(t.subType)});var c=t.getData();window.data=c,c.count()>l?o+=n(i("data.partialData"),{displayCnt:l}):o+=i("data.allData");for(var d=[],p=0;pn.blockIndex?n.step:null,o=i&&i.modDataCount;return{step:r,modBy:null!=o?Math.ceil(o/r):null,modDataCount:o}}},Tx.getPipeline=function(t){return this._pipelineMap.get(t)},Tx.updateStreamModes=function(t,e){var n=this._pipelineMap.get(t.uid),i=t.getData().count(),r=n.progressiveEnabled&&e.incrementalPrepareRender&&i>=n.threshold,o=t.get("large")&&i>=t.get("largeThreshold"),a="mod"===t.get("progressiveChunkMode")?i:null;t.pipelineContext=n.context={progressiveRender:r,modDataCount:a,large:o}},Tx.restorePipelines=function(t){var e=this,n=e._pipelineMap=N();t.eachSeries(function(t){var i=t.getProgressive(),r=t.uid;n.set(r,{id:r,head:null,tail:null,threshold:t.getProgressiveThreshold(),progressiveEnabled:i&&!(t.preventIncremental&&t.preventIncremental()),blockIndex:-1,step:Math.round(i||700),count:0}),Sa(e,t,t.dataTask)})},Tx.prepareStageTasks=function(){var t=this._stageTaskMap,e=this.ecInstance.getModel(),n=this.api;d(this._allHandlers,function(i){var r=t.get(i.uid)||t.set(i.uid,[]);i.reset&&pa(this,i,r,e,n),i.overallReset&&ga(this,i,r,e,n)},this)},Tx.prepareView=function(t,e,n,i){var r=t.renderTask,o=r.context;o.model=e,o.ecModel=n,o.api=i,r.__block=!t.incrementalPrepareRender,Sa(this,e,r)},Tx.performDataProcessorTasks=function(t,e){fa(this,this._dataProcessorHandlers,t,e,{block:!0})},Tx.performVisualTasks=function(t,e,n){fa(this,this._visualHandlers,t,e,n)},Tx.performSeriesTasks=function(t){var e;t.eachSeries(function(t){e|=t.dataTask.perform()}),this.unfinished|=e},Tx.plan=function(){this._pipelineMap.each(function(t){var e=t.tail;do{if(e.__block){t.blockIndex=e.__idxInPipeline;break}e=e.getUpstream()}while(e)})};var Dx=Tx.updatePayload=function(t,e){"remain"!==e&&(t.context.payload=e)},Ax=ba(0);da.wrapStageHandler=function(t,e){return x(t)&&(t={overallReset:t,seriesType:Ia(t)}),t.uid=vr("stageHandler"),e&&(t.visualType=e),t};var kx,Px={},Lx={};Ca(Px,Hy),Ca(Lx,vo),Px.eachSeriesByType=Px.eachRawSeriesByType=function(t){kx=t},Px.eachComponent=function(t){"series"===t.mainType&&t.subType&&(kx=t.subType)};var Ox=["#37A2DA","#32C5E9","#67E0E3","#9FE6B8","#FFDB5C","#ff9f7f","#fb7293","#E062AE","#E690D1","#e7bcf3","#9d96f5","#8378EA","#96BFFF"],zx={color:Ox,colorLayer:[["#37A2DA","#ffd85c","#fd7b5f"],["#37A2DA","#67E0E3","#FFDB5C","#ff9f7f","#E062AE","#9d96f5"],["#37A2DA","#32C5E9","#9FE6B8","#FFDB5C","#ff9f7f","#fb7293","#e7bcf3","#8378EA","#96BFFF"],Ox]},Ex=["#dd6b66","#759aa0","#e69d87","#8dc1a9","#ea7e53","#eedd78","#73a373","#73b9bc","#7289ab","#91ca8c","#f49f42"],Nx={color:Ex,backgroundColor:"#333",tooltip:{axisPointer:{lineStyle:{color:"#eee"},crossStyle:{color:"#eee"}}},legend:{textStyle:{color:"#eee"}},textStyle:{color:"#eee"},title:{textStyle:{color:"#eee"}},toolbox:{iconStyle:{normal:{borderColor:"#eee"}}},dataZoom:{textStyle:{color:"#eee"}},visualMap:{textStyle:{color:"#eee"}},timeline:{lineStyle:{color:"#eee"},itemStyle:{normal:{color:Ex[1]}},label:{normal:{textStyle:{color:"#eee"}}},controlStyle:{normal:{color:"#eee",borderColor:"#eee"}}},timeAxis:{axisLine:{lineStyle:{color:"#eee"}},axisTick:{lineStyle:{color:"#eee"}},axisLabel:{textStyle:{color:"#eee"}},splitLine:{lineStyle:{type:"dashed",color:"#aaa"}},splitArea:{areaStyle:{color:"#eee"}}},logAxis:{axisLine:{lineStyle:{color:"#eee"}},axisTick:{lineStyle:{color:"#eee"}},axisLabel:{textStyle:{color:"#eee"}},splitLine:{lineStyle:{type:"dashed",color:"#aaa"}},splitArea:{areaStyle:{color:"#eee"}}},valueAxis:{axisLine:{lineStyle:{color:"#eee"}},axisTick:{lineStyle:{color:"#eee"}},axisLabel:{textStyle:{color:"#eee"}},splitLine:{lineStyle:{type:"dashed",color:"#aaa"}},splitArea:{areaStyle:{color:"#eee"}}},categoryAxis:{axisLine:{lineStyle:{color:"#eee"}},axisTick:{lineStyle:{color:"#eee"}},axisLabel:{textStyle:{color:"#eee"}},splitLine:{lineStyle:{type:"dashed",color:"#aaa"}},splitArea:{areaStyle:{color:"#eee"}}},line:{symbol:"circle"},graph:{color:Ex},gauge:{title:{textStyle:{color:"#eee"}}},candlestick:{itemStyle:{normal:{color:"#FD1050",color0:"#0CF49B",borderColor:"#FD1050",borderColor0:"#0CF49B"}}}};Nx.categoryAxis.splitLine.show=!1,Iy.extend({type:"dataset",defaultOption:{seriesLayoutBy:Ry,sourceHeader:null,dimensions:null,source:null},optionUpdated:function(){Kr(this)}}),dx.extend({type:"dataset"});var Rx=P,Bx=d,Vx=x,Fx=w,Hx=Iy.parseClassType,Gx={zrender:"4.0.4"},Wx=1e3,Zx=1e3,Ux=3e3,Xx={PROCESSOR:{FILTER:Wx,STATISTIC:5e3},VISUAL:{LAYOUT:Zx,GLOBAL:2e3,CHART:Ux,COMPONENT:4e3,BRUSH:5e3}},jx="__flagInMainProcess",Yx="__optionUpdated",qx=/^[a-zA-Z0-9_]+$/;Da.prototype.on=Ta("on"),Da.prototype.off=Ta("off"),Da.prototype.one=Ta("one"),h(Da,Zp);var $x=Aa.prototype;$x._onframe=function(){if(!this._disposed){var t=this._scheduler;if(this[Yx]){var e=this[Yx].silent;this[jx]=!0,Pa(this),Kx.update.call(this),this[jx]=!1,this[Yx]=!1,Ea.call(this,e),Na.call(this,e)}else if(t.unfinished){var n=1,i=this._model;this._api;t.unfinished=!1;do{var r=+new Date;t.performSeriesTasks(i),t.performDataProcessorTasks(i),Oa(this,i),t.performVisualTasks(i),Ga(this,this._model,0,"remain"),n-=+new Date-r}while(n>0&&t.unfinished);t.unfinished||this._zr.flush()}}},$x.getDom=function(){return this._dom},$x.getZr=function(){return this._zr},$x.setOption=function(t,e,n){var i;if(Fx(e)&&(n=e.lazyUpdate,i=e.silent,e=e.notMerge),this[jx]=!0,!this._model||e){var r=new xo(this._api),o=this._theme,a=this._model=new Hy(null,null,o,r);a.scheduler=this._scheduler,a.init(null,null,o,r)}this._model.setOption(t,n_),n?(this[Yx]={silent:i},this[jx]=!1):(Pa(this),Kx.update.call(this),this._zr.flush(),this[Yx]=!1,this[jx]=!1,Ea.call(this,i),Na.call(this,i))},$x.setTheme=function(){console.log("ECharts#setTheme() is DEPRECATED in ECharts 3.0")},$x.getModel=function(){return this._model},$x.getOption=function(){return this._model&&this._model.getOption()},$x.getWidth=function(){return this._zr.getWidth()},$x.getHeight=function(){return this._zr.getHeight()},$x.getDevicePixelRatio=function(){return this._zr.painter.dpr||window.devicePixelRatio||1},$x.getRenderedCanvas=function(t){if(bp.canvasSupported)return(t=t||{}).pixelRatio=t.pixelRatio||1,t.backgroundColor=t.backgroundColor||this._model.get("backgroundColor"),this._zr.painter.getRenderedCanvas(t)},$x.getSvgDataUrl=function(){if(bp.svgSupported){var t=this._zr;return d(t.storage.getDisplayList(),function(t){t.stopAnimation(!0)}),t.painter.pathToDataUrl()}},$x.getDataURL=function(t){var e=(t=t||{}).excludeComponents,n=this._model,i=[],r=this;Bx(e,function(t){n.eachComponent({mainType:t},function(t){var e=r._componentsMap[t.__viewId];e.group.ignore||(i.push(e),e.group.ignore=!0)})});var o="svg"===this._zr.painter.getType()?this.getSvgDataUrl():this.getRenderedCanvas(t).toDataURL("image/"+(t&&t.type||"png"));return Bx(i,function(t){t.group.ignore=!1}),o},$x.getConnectedDataURL=function(t){if(bp.canvasSupported){var e=this.group,i=Math.min,r=Math.max;if(l_[e]){var o=1/0,a=1/0,s=-1/0,l=-1/0,u=[],h=t&&t.pixelRatio||1;d(s_,function(h,c){if(h.group===e){var d=h.getRenderedCanvas(n(t)),f=h.getDom().getBoundingClientRect();o=i(f.left,o),a=i(f.top,a),s=r(f.right,s),l=r(f.bottom,l),u.push({dom:d,left:f.left,top:f.top})}});var c=(s*=h)-(o*=h),f=(l*=h)-(a*=h),p=Op();p.width=c,p.height=f;var g=mn(p);return Bx(u,function(t){var e=new je({style:{x:t.left*h-o,y:t.top*h-a,image:t.dom}});g.add(e)}),g.refreshImmediately(),p.toDataURL("image/"+(t&&t.type||"png"))}return this.getDataURL(t)}},$x.convertToPixel=v(ka,"convertToPixel"),$x.convertFromPixel=v(ka,"convertFromPixel"),$x.containPixel=function(t,e){var n;return t=An(this._model,t),d(t,function(t,i){i.indexOf("Models")>=0&&d(t,function(t){var r=t.coordinateSystem;if(r&&r.containPoint)n|=!!r.containPoint(e);else if("seriesModels"===i){var o=this._chartsMap[t.__viewId];o&&o.containPoint&&(n|=o.containPoint(e,t))}},this)},this),!!n},$x.getVisual=function(t,e){var n=(t=An(this._model,t,{defaultMainType:"series"})).seriesModel.getData(),i=t.hasOwnProperty("dataIndexInside")?t.dataIndexInside:t.hasOwnProperty("dataIndex")?n.indexOfRawIndex(t.dataIndex):null;return null!=i?n.getItemVisual(i,e):n.getVisual(e)},$x.getViewOfComponentModel=function(t){return this._componentsMap[t.__viewId]},$x.getViewOfSeriesModel=function(t){return this._chartsMap[t.__viewId]};var Kx={prepareAndUpdate:function(t){Pa(this),Kx.update.call(this,t)},update:function(t){var e=this._model,n=this._api,i=this._zr,r=this._coordSysMgr,o=this._scheduler;if(e){o.restoreData(e,t),o.performSeriesTasks(e),r.create(e,n),o.performDataProcessorTasks(e,t),Oa(this,e),r.update(e,n),Va(e),o.performVisualTasks(e,t),Fa(this,e,n,t);var a=e.get("backgroundColor")||"transparent";if(bp.canvasSupported)i.setBackgroundColor(a);else{var s=St(a);a=Lt(s,"rgb"),0===s[3]&&(a="transparent")}Wa(e,n)}},updateTransform:function(t){var e=this._model,n=this,i=this._api;if(e){var r=[];e.eachComponent(function(o,a){var s=n.getViewOfComponentModel(a);if(s&&s.__alive)if(s.updateTransform){var l=s.updateTransform(a,e,i,t);l&&l.update&&r.push(s)}else r.push(s)});var o=N();e.eachSeries(function(r){var a=n._chartsMap[r.__viewId];if(a.updateTransform){var s=a.updateTransform(r,e,i,t);s&&s.update&&o.set(r.uid,1)}else o.set(r.uid,1)}),Va(e),this._scheduler.performVisualTasks(e,t,{setDirty:!0,dirtyMap:o}),Ga(n,e,0,t,o),Wa(e,this._api)}},updateView:function(t){var e=this._model;e&&(ra.markUpdateMethod(t,"updateView"),Va(e),this._scheduler.performVisualTasks(e,t,{setDirty:!0}),Fa(this,this._model,this._api,t),Wa(e,this._api))},updateVisual:function(t){Kx.update.call(this,t)},updateLayout:function(t){Kx.update.call(this,t)}};$x.resize=function(t){this._zr.resize(t);var e=this._model;if(this._loadingFX&&this._loadingFX.resize(),e){var n=e.resetOption("media"),i=t&&t.silent;this[jx]=!0,n&&Pa(this),Kx.update.call(this),this[jx]=!1,Ea.call(this,i),Na.call(this,i)}},$x.showLoading=function(t,e){if(Fx(t)&&(e=t,t=""),t=t||"default",this.hideLoading(),a_[t]){var n=a_[t](this._api,e),i=this._zr;this._loadingFX=n,i.add(n)}},$x.hideLoading=function(){this._loadingFX&&this._zr.remove(this._loadingFX),this._loadingFX=null},$x.makeActionFromEvent=function(t){var e=o({},t);return e.type=t_[t.type],e},$x.dispatchAction=function(t,e){Fx(e)||(e={silent:!!e}),Jx[t.type]&&this._model&&(this[jx]?this._pendingActions.push(t):(za.call(this,t,e.silent),e.flush?this._zr.flush(!0):!1!==e.flush&&bp.browser.weChat&&this._throttledZrFlush(),Ea.call(this,e.silent),Na.call(this,e.silent)))},$x.appendData=function(t){var e=t.seriesIndex;this.getModel().getSeriesByIndex(e).appendData(t),this._scheduler.unfinished=!0},$x.on=Ta("on"),$x.off=Ta("off"),$x.one=Ta("one");var Qx=["click","dblclick","mouseover","mouseout","mousemove","mousedown","mouseup","globalout","contextmenu"];$x._initEvents=function(){Bx(Qx,function(t){this._zr.on(t,function(e){var n,i=this.getModel(),r=e.target;if("globalout"===t)n={};else if(r&&null!=r.dataIndex){var a=r.dataModel||i.getSeriesByIndex(r.seriesIndex);n=a&&a.getDataParams(r.dataIndex,r.dataType)||{}}else r&&r.eventData&&(n=o({},r.eventData));n&&(n.event=e,n.type=t,this.trigger(t,n))},this)},this),Bx(t_,function(t,e){this._messageCenter.on(e,function(t){this.trigger(e,t)},this)},this)},$x.isDisposed=function(){return this._disposed},$x.clear=function(){this.setOption({series:[]},!0)},$x.dispose=function(){if(!this._disposed){this._disposed=!0,Pn(this.getDom(),c_,"");var t=this._api,e=this._model;Bx(this._componentsViews,function(n){n.dispose(e,t)}),Bx(this._chartsViews,function(n){n.dispose(e,t)}),this._zr.dispose(),delete s_[this.id]}},h(Aa,Zp);var Jx={},t_={},e_=[],n_=[],i_=[],r_=[],o_={},a_={},s_={},l_={},u_=new Date-0,h_=new Date-0,c_="_echarts_instance_",d_={},f_=qa;ns(2e3,Mx),Qa(ex),Ja(5e3,function(t){var e=N();t.eachSeries(function(t){var n=t.get("stack");if(n){var i=e.get(n)||e.set(n,[]),r=t.getData(),o={stackResultDimension:r.getCalculationInfo("stackResultDimension"),stackedOverDimension:r.getCalculationInfo("stackedOverDimension"),stackedDimension:r.getCalculationInfo("stackedDimension"),stackedByDimension:r.getCalculationInfo("stackedByDimension"),isStackedByIndex:r.getCalculationInfo("isStackedByIndex"),data:r,seriesModel:t};if(!o.stackedDimension||!o.isStackedByIndex&&!o.stackedByDimension)return;i.length&&r.setCalculationInfo("stackedOnSeries",i[i.length-1].seriesModel),i.push(o)}}),e.each(No)}),rs("default",function(t,e){a(e=e||{},{text:"loading",color:"#c23531",textColor:"#000",maskColor:"rgba(255, 255, 255, 0.8)",zlevel:0});var n=new Fv({style:{fill:e.maskColor},zlevel:e.zlevel,z:1e4}),i=new Zv({shape:{startAngle:-Cx/2,endAngle:-Cx/2+.1,r:10},style:{stroke:e.color,lineCap:"round",lineWidth:5},zlevel:e.zlevel,z:10001}),r=new Fv({style:{fill:"none",text:e.text,textPosition:"right",textDistance:10,textFill:e.textColor},zlevel:e.zlevel,z:10001});i.animateShape(!0).when(1e3,{endAngle:3*Cx/2}).start("circularInOut"),i.animateShape(!0).when(1e3,{startAngle:3*Cx/2}).delay(300).start("circularInOut");var o=new Sg;return o.add(i),o.add(r),o.add(n),o.resize=function(){var e=t.getWidth()/2,o=t.getHeight()/2;i.setShape({cx:e,cy:o});var a=i.shape.r;r.setShape({x:e-a,y:o-a,width:2*a,height:2*a}),n.setShape({x:0,y:0,width:t.getWidth(),height:t.getHeight()})},o.resize(),o}),ts({type:"highlight",event:"highlight",update:"highlight"},R),ts({type:"downplay",event:"downplay",update:"downplay"},R),Ka("light",zx),Ka("dark",Nx);var p_={};hs.prototype={constructor:hs,add:function(t){return this._add=t,this},update:function(t){return this._update=t,this},remove:function(t){return this._remove=t,this},execute:function(){var t=this._old,e=this._new,n={},i=[],r=[];for(cs(t,{},i,"_oldKeyGetter",this),cs(e,n,r,"_newKeyGetter",this),o=0;o=e)){for(var n,i=this._chunkSize,r=this._rawData,o=this._storage,a=this.dimensions,s=a.length,l=this._dimensionInfos,u=this._nameList,h=this._idList,c=this._rawExtent,d=this._nameRepeatCount={},f=this._chunkCount,p=f-1,g=0;gA[1]&&(A[1]=D)}if(!r.pure){var k=u[b];if(w&&null==k)if(null!=w.name)u[b]=k=w.name;else if(null!=n){var P=a[n],L=o[P][M];if(L){k=L[S];var O=l[P].ordinalMeta;O&&O.categories.length&&(k=O.categories[k])}}var z=null==w?null:w.id;null==z&&null!=k&&(d[k]=d[k]||0,z=k,d[k]>0&&(z+="__ec__"+d[k]),d[k]++),null!=z&&(h[b]=z)}}!r.persistent&&r.clean&&r.clean(),this._rawCount=this._count=e,this._extent={},ys(this)}},S_.count=function(){return this._count},S_.getIndices=function(){var t=this._indices;if(t){var e=t.constructor,n=this._count;if(e===Array){i=new e(n);for(r=0;r=0&&e=0&&eo&&(o=s)}return n=[r,o],this._extent[t]=n,n},S_.getApproximateExtent=function(t){return t=this.getDimension(t),this._approximateExtent[t]||this.getDataExtent(t)},S_.setApproximateExtent=function(t,e){e=this.getDimension(e),this._approximateExtent[e]=t.slice()},S_.getCalculationInfo=function(t){return this._calculationInfo[t]},S_.setCalculationInfo=function(t,e){m_(t)?o(this._calculationInfo,t):this._calculationInfo[t]=e},S_.getSum=function(t){var e=0;if(this._storage[t])for(var n=0,i=this.count();n=this._rawCount||t<0)return-1;var e=this._indices,n=e[t];if(null!=n&&nt))return o;r=o-1}}return-1},S_.indicesOfNearest=function(t,e,n){var i=[];if(!this._storage[t])return i;null==n&&(n=1/0);for(var r=Number.MAX_VALUE,o=-1,a=0,s=this.count();a=0&&o<0)&&(r=u,o=l,i.length=0),i.push(a))}return i},S_.getRawIndex=_s,S_.getRawDataItem=function(t){if(this._rawData.persistent)return this._rawData.getItem(this.getRawIndex(t));for(var e=[],n=0;n=l&&w<=u||isNaN(w))&&(o[a++]=c),c++;h=!0}else if(2===i){for(var d=this._storage[s],v=this._storage[e[1]],y=t[e[1]][0],x=t[e[1]][1],f=0;f=l&&w<=u||isNaN(w))&&(b>=y&&b<=x||isNaN(b))&&(o[a++]=c),c++}h=!0}}if(!h)if(1===i)for(m=0;m=l&&w<=u||isNaN(w))&&(o[a++]=S)}else for(m=0;mt[I][1])&&(M=!1)}M&&(o[a++]=this.getRawIndex(m))}return ab[1]&&(b[1]=w)}}}return r},S_.downSample=function(t,e,n,i){for(var r=Ss(this,[t]),o=r._storage,a=[],s=Math.floor(1/e),l=o[t],u=this.count(),h=this._chunkSize,c=r._rawExtent[t],d=new(gs(this))(u),f=0,p=0;pu-p&&(s=u-p,a.length=s);for(var g=0;gc[1]&&(c[1]=x),d[f++]=_}return r._count=f,r._indices=d,r.getRawIndex=ws,r},S_.getItemModel=function(t){var e=this.hostModel;return new pr(this.getRawDataItem(t),e,e&&e.ecModel)},S_.diff=function(t){var e=this;return new hs(t?t.getIndices():[],this.getIndices(),function(e){return bs(t,e)},function(t){return bs(e,t)})},S_.getVisual=function(t){var e=this._visual;return e&&e[t]},S_.setVisual=function(t,e){if(m_(t))for(var n in t)t.hasOwnProperty(n)&&this.setVisual(n,t[n]);else this._visual=this._visual||{},this._visual[t]=e},S_.setLayout=function(t,e){if(m_(t))for(var n in t)t.hasOwnProperty(n)&&this.setLayout(n,t[n]);else this._layout[t]=e},S_.getLayout=function(t){return this._layout[t]},S_.getItemLayout=function(t){return this._itemLayouts[t]},S_.setItemLayout=function(t,e,n){this._itemLayouts[t]=n?o(this._itemLayouts[t]||{},e):e},S_.clearItemLayouts=function(){this._itemLayouts.length=0},S_.getItemVisual=function(t,e,n){var i=this._itemVisuals[t],r=i&&i[e];return null!=r||n?r:this.getVisual(e)},S_.setItemVisual=function(t,e,n){var i=this._itemVisuals[t]||{},r=this.hasItemVisual;if(this._itemVisuals[t]=i,m_(e))for(var o in e)e.hasOwnProperty(o)&&(i[o]=e[o],r[o]=!0);else i[e]=n,r[e]=!0},S_.clearAllVisual=function(){this._visual={},this._itemVisuals=[],this.hasItemVisual={}};var I_=function(t){t.seriesIndex=this.seriesIndex,t.dataIndex=this.dataIndex,t.dataType=this.dataType};S_.setItemGraphicEl=function(t,e){var n=this.hostModel;e&&(e.dataIndex=t,e.dataType=this.dataType,e.seriesIndex=n&&n.seriesIndex,"group"===e.type&&e.traverse(I_,e)),this._graphicEls[t]=e},S_.getItemGraphicEl=function(t){return this._graphicEls[t]},S_.eachItemGraphicEl=function(t,e){d(this._graphicEls,function(n,i){n&&t&&t.call(e,n,i)})},S_.cloneShallow=function(t){if(!t){var e=f(this.dimensions,this.getDimensionInfo,this);t=new M_(e,this.hostModel)}if(t._storage=this._storage,vs(t,this),this._indices){var n=this._indices.constructor;t._indices=new n(this._indices)}else t._indices=null;return t.getRawIndex=t._indices?ws:_s,t},S_.wrapMethod=function(t,e){var n=this[t];"function"==typeof n&&(this.__wrappedMethods=this.__wrappedMethods||[],this.__wrappedMethods.push(t),this[t]=function(){var t=n.apply(this,arguments);return e.apply(this,[t].concat(A(arguments)))})},S_.TRANSFERABLE_METHODS=["cloneShallow","downSample","map"],S_.CHANGABLE_METHODS=["filterSelf","selectRange"];var C_=function(t,e){return e=e||{},Ts(e.coordDimensions||[],t,{dimsDef:e.dimensionsDefine||t.dimensionsDefine,encodeDef:e.encodeDefine||t.encodeDefine,dimCount:e.dimensionsCount,generateCoord:e.generateCoord,generateCoordCount:e.generateCoordCount})};Ns.prototype.parse=function(t){return t},Ns.prototype.getSetting=function(t){return this._setting[t]},Ns.prototype.contain=function(t){var e=this._extent;return t>=e[0]&&t<=e[1]},Ns.prototype.normalize=function(t){var e=this._extent;return e[1]===e[0]?.5:(t-e[0])/(e[1]-e[0])},Ns.prototype.scale=function(t){var e=this._extent;return t*(e[1]-e[0])+e[0]},Ns.prototype.unionExtent=function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1])},Ns.prototype.unionExtentFromData=function(t,e){this.unionExtent(t.getApproximateExtent(e))},Ns.prototype.getExtent=function(){return this._extent.slice()},Ns.prototype.setExtent=function(t,e){var n=this._extent;isNaN(t)||(n[0]=t),isNaN(e)||(n[1]=e)},Ns.prototype.isBlank=function(){return this._isBlank},Ns.prototype.setBlank=function(t){this._isBlank=t},Ns.prototype.getLabel=null,En(Ns),Vn(Ns,{registerWhenExtend:!0}),Rs.createByAxisModel=function(t){var e=t.option,n=e.data,i=n&&f(n,Vs);return new Rs({categories:i,needCollect:!i,deduplication:!1!==e.dedplication})};var T_=Rs.prototype;T_.getOrdinal=function(t){return Bs(this).get(t)},T_.parseAndCollect=function(t){var e,n=this._needCollect;if("string"!=typeof t&&!n)return t;if(n&&!this._deduplication)return e=this.categories.length,this.categories[e]=t,e;var i=Bs(this);return null==(e=i.get(t))&&(n?(e=this.categories.length,this.categories[e]=t,i.set(t,e)):e=NaN),e};var D_=Ns.prototype,A_=Ns.extend({type:"ordinal",init:function(t,e){t&&!y(t)||(t=new Rs({categories:t})),this._ordinalMeta=t,this._extent=e||[0,t.categories.length-1]},parse:function(t){return"string"==typeof t?this._ordinalMeta.getOrdinal(t):Math.round(t)},contain:function(t){return t=this.parse(t),D_.contain.call(this,t)&&null!=this._ordinalMeta.categories[t]},normalize:function(t){return D_.normalize.call(this,this.parse(t))},scale:function(t){return Math.round(D_.scale.call(this,t))},getTicks:function(){for(var t=[],e=this._extent,n=e[0];n<=e[1];)t.push(n),n++;return t},getLabel:function(t){if(!this.isBlank())return this._ordinalMeta.categories[t]},count:function(){return this._extent[1]-this._extent[0]+1},unionExtentFromData:function(t,e){this.unionExtent(t.getApproximateExtent(e))},getOrdinalMeta:function(){return this._ordinalMeta},niceTicks:R,niceExtent:R});A_.create=function(){return new A_};var k_=wr,P_=wr,L_=Ns.extend({type:"interval",_interval:0,_intervalPrecision:2,setExtent:function(t,e){var n=this._extent;isNaN(t)||(n[0]=parseFloat(t)),isNaN(e)||(n[1]=parseFloat(e))},unionExtent:function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1]),L_.prototype.setExtent.call(this,e[0],e[1])},getInterval:function(){return this._interval},setInterval:function(t){this._interval=t,this._niceExtent=this._extent.slice(),this._intervalPrecision=Hs(t)},getTicks:function(){return Zs(this._interval,this._extent,this._niceExtent,this._intervalPrecision)},getLabel:function(t,e){if(null==t)return"";var n=e&&e.precision;return null==n?n=Sr(t)||0:"auto"===n&&(n=this._intervalPrecision),t=P_(t,n,!0),Or(t)},niceTicks:function(t,e,n){t=t||5;var i=this._extent,r=i[1]-i[0];if(isFinite(r)){r<0&&(r=-r,i.reverse());var o=Fs(i,t,e,n);this._intervalPrecision=o.intervalPrecision,this._interval=o.interval,this._niceExtent=o.niceTickExtent}},niceExtent:function(t){var e=this._extent;if(e[0]===e[1])if(0!==e[0]){var n=e[0];t.fixMax?e[0]-=n/2:(e[1]+=n/2,e[0]-=n/2)}else e[1]=1;var i=e[1]-e[0];isFinite(i)||(e[0]=0,e[1]=1),this.niceTicks(t.splitNumber,t.minInterval,t.maxInterval);var r=this._interval;t.fixMin||(e[0]=P_(Math.floor(e[0]/r)*r)),t.fixMax||(e[1]=P_(Math.ceil(e[1]/r)*r))}});L_.create=function(){return new L_};var O_="__ec_stack_",z_="undefined"!=typeof Float32Array?Float32Array:Array,E_={seriesType:"bar",plan:px(),reset:function(t){if(Ks(t)&&Qs(t)){var e=t.getData(),n=t.coordinateSystem,i=n.getBaseAxis(),r=n.getOtherAxis(i),o=e.mapDimension(r.dim),a=e.mapDimension(i.dim),s=r.isHorizontal(),l=s?0:1,u=$s(Ys([t]),i,t).width;return u>.5||(u=.5),{progress:function(t,e){for(var h,c=new z_(2*t.count),d=[],f=[],p=0;null!=(h=t.next());)f[l]=e.get(o,h),f[1-l]=e.get(a,h),d=n.dataToPoint(f,null,d),c[p++]=d[0],c[p++]=d[1];e.setLayout({largePoints:c,barWidth:u,valueAxisStart:Js(i,r,!1),valueAxisHorizontal:s})}}}}},N_=L_.prototype,R_=Math.ceil,B_=Math.floor,V_=function(t,e,n,i){for(;n>>1;t[r][1]n&&(o=n);var a=H_.length,s=V_(H_,o,0,a),l=H_[Math.min(s,a-1)],u=l[1];"year"===l[0]&&(u*=Lr(r/u/t,!0));var h=this.getSetting("useUTC")?0:60*new Date(+i[0]||+i[1]).getTimezoneOffset()*1e3,c=[Math.round(R_((i[0]-h)/u)*u+h),Math.round(B_((i[1]-h)/u)*u+h)];Ws(c,i),this._stepLvl=l,this._interval=u,this._niceExtent=c},parse:function(t){return+Ar(t)}});d(["contain","normalize"],function(t){F_.prototype[t]=function(e){return N_[t].call(this,this.parse(e))}});var H_=[["hh:mm:ss",1e3],["hh:mm:ss",5e3],["hh:mm:ss",1e4],["hh:mm:ss",15e3],["hh:mm:ss",3e4],["hh:mm\nMM-dd",6e4],["hh:mm\nMM-dd",3e5],["hh:mm\nMM-dd",6e5],["hh:mm\nMM-dd",9e5],["hh:mm\nMM-dd",18e5],["hh:mm\nMM-dd",36e5],["hh:mm\nMM-dd",72e5],["hh:mm\nMM-dd",216e5],["hh:mm\nMM-dd",432e5],["MM-dd\nyyyy",864e5],["MM-dd\nyyyy",1728e5],["MM-dd\nyyyy",2592e5],["MM-dd\nyyyy",3456e5],["MM-dd\nyyyy",432e6],["MM-dd\nyyyy",5184e5],["week",6048e5],["MM-dd\nyyyy",864e6],["week",12096e5],["week",18144e5],["month",26784e5],["week",36288e5],["month",53568e5],["week",36288e5],["quarter",8208e6],["month",107136e5],["month",13392e6],["half-year",16416e6],["month",214272e5],["month",26784e6],["year",32832e6]];F_.create=function(t){return new F_({useUTC:t.ecModel.get("useUTC")})};var G_=Ns.prototype,W_=L_.prototype,Z_=Sr,U_=wr,X_=Math.floor,j_=Math.ceil,Y_=Math.pow,q_=Math.log,$_=Ns.extend({type:"log",base:10,$constructor:function(){Ns.apply(this,arguments),this._originalScale=new L_},getTicks:function(){var t=this._originalScale,e=this._extent,n=t.getExtent();return f(W_.getTicks.call(this),function(i){var r=wr(Y_(this.base,i));return r=i===e[0]&&t.__fixMin?tl(r,n[0]):r,r=i===e[1]&&t.__fixMax?tl(r,n[1]):r},this)},getLabel:W_.getLabel,scale:function(t){return t=G_.scale.call(this,t),Y_(this.base,t)},setExtent:function(t,e){var n=this.base;t=q_(t)/q_(n),e=q_(e)/q_(n),W_.setExtent.call(this,t,e)},getExtent:function(){var t=this.base,e=G_.getExtent.call(this);e[0]=Y_(t,e[0]),e[1]=Y_(t,e[1]);var n=this._originalScale,i=n.getExtent();return n.__fixMin&&(e[0]=tl(e[0],i[0])),n.__fixMax&&(e[1]=tl(e[1],i[1])),e},unionExtent:function(t){this._originalScale.unionExtent(t);var e=this.base;t[0]=q_(t[0])/q_(e),t[1]=q_(t[1])/q_(e),G_.unionExtent.call(this,t)},unionExtentFromData:function(t,e){this.unionExtent(t.getApproximateExtent(e))},niceTicks:function(t){t=t||10;var e=this._extent,n=e[1]-e[0];if(!(n===1/0||n<=0)){var i=kr(n);for(t/n*i<=.5&&(i*=10);!isNaN(i)&&Math.abs(i)<1&&Math.abs(i)>0;)i*=10;var r=[wr(j_(e[0]/i)*i),wr(X_(e[1]/i)*i)];this._interval=i,this._niceExtent=r}},niceExtent:function(t){W_.niceExtent.call(this,t);var e=this._originalScale;e.__fixMin=t.fixMin,e.__fixMax=t.fixMax}});d(["contain","normalize"],function(t){$_.prototype[t]=function(e){return e=q_(e)/q_(this.base),G_[t].call(this,e)}}),$_.create=function(){return new $_};var K_={getMin:function(t){var e=this.option,n=t||null==e.rangeStart?e.min:e.rangeStart;return this.axis&&null!=n&&"dataMin"!==n&&"function"!=typeof n&&!I(n)&&(n=this.axis.scale.parse(n)),n},getMax:function(t){var e=this.option,n=t||null==e.rangeEnd?e.max:e.rangeEnd;return this.axis&&null!=n&&"dataMax"!==n&&"function"!=typeof n&&!I(n)&&(n=this.axis.scale.parse(n)),n},getNeedCrossZero:function(){var t=this.option;return null==t.rangeStart&&null==t.rangeEnd&&!t.scale},getCoordSysModel:R,setRange:function(t,e){this.option.rangeStart=t,this.option.rangeEnd=e},resetRange:function(){this.option.rangeStart=this.option.rangeEnd=null}},Q_=Ai({type:"triangle",shape:{cx:0,cy:0,width:0,height:0},buildPath:function(t,e){var n=e.cx,i=e.cy,r=e.width/2,o=e.height/2;t.moveTo(n,i-o),t.lineTo(n+r,i+o),t.lineTo(n-r,i+o),t.closePath()}}),J_=Ai({type:"diamond",shape:{cx:0,cy:0,width:0,height:0},buildPath:function(t,e){var n=e.cx,i=e.cy,r=e.width/2,o=e.height/2;t.moveTo(n,i-o),t.lineTo(n+r,i),t.lineTo(n,i+o),t.lineTo(n-r,i),t.closePath()}}),tw=Ai({type:"pin",shape:{x:0,y:0,width:0,height:0},buildPath:function(t,e){var n=e.x,i=e.y,r=e.width/5*3,o=Math.max(r,e.height),a=r/2,s=a*a/(o-a),l=i-o+a+s,u=Math.asin(s/a),h=Math.cos(u)*a,c=Math.sin(u),d=Math.cos(u),f=.6*a,p=.7*a;t.moveTo(n-h,l+s),t.arc(n,l,a,Math.PI-u,2*Math.PI+u),t.bezierCurveTo(n+h-c*f,l+s+d*f,n,i-p,n,i),t.bezierCurveTo(n,i-p,n-h+c*f,l+s+d*f,n-h,l+s),t.closePath()}}),ew=Ai({type:"arrow",shape:{x:0,y:0,width:0,height:0},buildPath:function(t,e){var n=e.height,i=e.width,r=e.x,o=e.y,a=i/3*2;t.moveTo(r,o),t.lineTo(r+a,o+n),t.lineTo(r,o+n/4*3),t.lineTo(r-a,o+n),t.lineTo(r,o),t.closePath()}}),nw={line:function(t,e,n,i,r){r.x1=t,r.y1=e+i/2,r.x2=t+n,r.y2=e+i/2},rect:function(t,e,n,i,r){r.x=t,r.y=e,r.width=n,r.height=i},roundRect:function(t,e,n,i,r){r.x=t,r.y=e,r.width=n,r.height=i,r.r=Math.min(n,i)/4},square:function(t,e,n,i,r){var o=Math.min(n,i);r.x=t,r.y=e,r.width=o,r.height=o},circle:function(t,e,n,i,r){r.cx=t+n/2,r.cy=e+i/2,r.r=Math.min(n,i)/2},diamond:function(t,e,n,i,r){r.cx=t+n/2,r.cy=e+i/2,r.width=n,r.height=i},pin:function(t,e,n,i,r){r.x=t+n/2,r.y=e+i/2,r.width=n,r.height=i},arrow:function(t,e,n,i,r){r.x=t+n/2,r.y=e+i/2,r.width=n,r.height=i},triangle:function(t,e,n,i,r){r.cx=t+n/2,r.cy=e+i/2,r.width=n,r.height=i}},iw={};d({line:Hv,rect:Fv,roundRect:Fv,square:Fv,circle:Pv,diamond:J_,pin:tw,arrow:ew,triangle:Q_},function(t,e){iw[e]=new t});var rw=Ai({type:"symbol",shape:{symbolType:"",x:0,y:0,width:0,height:0},beforeBrush:function(){var t=this.style;"pin"===this.shape.symbolType&&"inside"===t.textPosition&&(t.textPosition=["50%","40%"],t.textAlign="center",t.textVerticalAlign="middle")},buildPath:function(t,e,n){var i=e.symbolType,r=iw[i];"none"!==e.symbolType&&(r||(r=iw[i="rect"]),nw[i](e.x,e.y,e.width,e.height,r.shape),r.buildPath(t,r.shape,n))}}),ow={isDimensionStacked:Ps,enableDataStack:ks,getStackedDimension:Ls},aw=(Object.freeze||Object)({createList:function(t){return Os(t.getSource(),t)},getLayoutRect:Gr,dataStack:ow,createScale:function(t,e){var n=e;pr.isInstance(e)||h(n=new pr(e),K_);var i=rl(n);return i.setExtent(t[0],t[1]),il(i,n),i},mixinAxisModelCommonMethods:function(t){h(t,K_)},completeDimensions:Ts,createDimensions:C_,createSymbol:cl}),sw=1e-8;pl.prototype={constructor:pl,properties:null,getBoundingRect:function(){var t=this._rect;if(t)return t;for(var e=Number.MAX_VALUE,n=[e,e],i=[-e,-e],r=[],o=[],a=this.geometries,s=0;s0}),function(t){var e=t.properties,n=t.geometry,i=n.coordinates,r=[];"Polygon"===n.type&&r.push({type:"polygon",exterior:i[0],interiors:i.slice(1)}),"MultiPolygon"===n.type&&d(i,function(t){t[0]&&r.push({type:"polygon",exterior:t[0],interiors:t.slice(1)})});var o=new pl(e.name,r,e.cp);return o.properties=e,o})},uw=Dn(),hw=[0,1],cw=function(t,e,n){this.dim=t,this.scale=e,this._extent=n||[0,0],this.inverse=!1,this.onBand=!1};cw.prototype={constructor:cw,contain:function(t){var e=this._extent,n=Math.min(e[0],e[1]),i=Math.max(e[0],e[1]);return t>=n&&t<=i},containData:function(t){return this.contain(this.dataToCoord(t))},getExtent:function(){return this._extent.slice()},getPixelPrecision:function(t){return Ir(t||this.scale.getExtent(),this._extent)},setExtent:function(t,e){var n=this._extent;n[0]=t,n[1]=e},dataToCoord:function(t,e){var n=this._extent,i=this.scale;return t=i.normalize(t),this.onBand&&"ordinal"===i.type&&Ll(n=n.slice(),i.count()),xr(t,hw,n,e)},coordToData:function(t,e){var n=this._extent,i=this.scale;this.onBand&&"ordinal"===i.type&&Ll(n=n.slice(),i.count());var r=xr(t,n,hw,e);return this.scale.scale(r)},pointToData:function(t,e){},getTicksCoords:function(t){var e=(t=t||{}).tickModel||this.getTickModel(),n=yl(this,e),i=f(n.ticks,function(t){return{coord:this.dataToCoord(t),tickValue:t}},this),r=e.get("alignWithLabel");return Ol(this,i,n.tickCategoryInterval,r,t.clamp),i},getViewLabels:function(){return vl(this).labels},getLabelModel:function(){return this.model.getModel("axisLabel")},getTickModel:function(){return this.model.getModel("axisTick")},getBandWidth:function(){var t=this._extent,e=this.scale.getExtent(),n=e[1]-e[0]+(this.onBand?1:0);0===n&&(n=1);var i=Math.abs(t[1]-t[0]);return Math.abs(i)/n},isHorizontal:null,getRotate:null,calculateCategoryInterval:function(){return Tl(this)}};var dw=lw,fw={};d(["map","each","filter","indexOf","inherits","reduce","filter","bind","curry","isArray","isString","isObject","isFunction","extend","defaults","clone","merge"],function(t){fw[t]=Np[t]}),cx.extend({type:"series.line",dependencies:["grid","polar"],getInitialData:function(t,e){return Os(this.getSource(),this)},defaultOption:{zlevel:0,z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,hoverAnimation:!0,clipOverflow:!0,label:{position:"top"},lineStyle:{width:2,type:"solid"},step:!1,smooth:!1,smoothMonotone:null,symbol:"emptyCircle",symbolSize:4,symbolRotate:null,showSymbol:!0,showAllSymbol:"auto",connectNulls:!1,sampling:"none",animationEasing:"linear",progressive:0,hoverLayerThreshold:1/0}});var pw=El.prototype,gw=El.getSymbolSize=function(t,e){var n=t.getItemVisual(e,"symbolSize");return n instanceof Array?n.slice():[+n,+n]};pw._createSymbol=function(t,e,n,i,r){this.removeAll();var o=cl(t,-1,-1,2,2,e.getItemVisual(n,"color"),r);o.attr({z2:100,culling:!0,scale:Nl(i)}),o.drift=Rl,this._symbolType=t,this.add(o)},pw.stopSymbolAnimation=function(t){this.childAt(0).stopAnimation(t)},pw.getSymbolPath=function(){return this.childAt(0)},pw.getScale=function(){return this.childAt(0).scale},pw.highlight=function(){this.childAt(0).trigger("emphasis")},pw.downplay=function(){this.childAt(0).trigger("normal")},pw.setZ=function(t,e){var n=this.childAt(0);n.zlevel=t,n.z=e},pw.setDraggable=function(t){var e=this.childAt(0);e.draggable=t,e.cursor=t?"move":"pointer"},pw.updateData=function(t,e,n){this.silent=!1;var i=t.getItemVisual(e,"symbol")||"circle",r=t.hostModel,o=gw(t,e),a=i!==this._symbolType;if(a){var s=t.getItemVisual(e,"symbolKeepAspect");this._createSymbol(i,t,e,o,s)}else(l=this.childAt(0)).silent=!1,ar(l,{scale:Nl(o)},r,e);if(this._updateCommon(t,e,o,n),a){var l=this.childAt(0),u=n&&n.fadeIn,h={scale:l.scale.slice()};u&&(h.style={opacity:l.style.opacity}),l.scale=[0,0],u&&(l.style.opacity=0),sr(l,h,r,e)}this._seriesModel=r};var mw=["itemStyle"],vw=["emphasis","itemStyle"],yw=["label"],xw=["emphasis","label"];pw._updateCommon=function(t,e,n,i){var r=this.childAt(0),a=t.hostModel,s=t.getItemVisual(e,"color");"image"!==r.type&&r.useStyle({strokeNoScale:!0});var l=i&&i.itemStyle,u=i&&i.hoverItemStyle,h=i&&i.symbolRotate,c=i&&i.symbolOffset,d=i&&i.labelModel,f=i&&i.hoverLabelModel,p=i&&i.hoverAnimation,g=i&&i.cursorStyle;if(!i||t.hasItemOption){var m=i&&i.itemModel?i.itemModel:t.getItemModel(e);l=m.getModel(mw).getItemStyle(["color"]),u=m.getModel(vw).getItemStyle(),h=m.getShallow("symbolRotate"),c=m.getShallow("symbolOffset"),d=m.getModel(yw),f=m.getModel(xw),p=m.getShallow("hoverAnimation"),g=m.getShallow("cursor")}else u=o({},u);var v=r.style;r.attr("rotation",(h||0)*Math.PI/180||0),c&&r.attr("position",[_r(c[0],n[0]),_r(c[1],n[1])]),g&&r.attr("cursor",g),r.setColor(s,i&&i.symbolInnerColor),r.setStyle(l);var y=t.getItemVisual(e,"opacity");null!=y&&(v.opacity=y);var x=t.getItemVisual(e,"liftZ"),_=r.__z2Origin;null!=x?null==_&&(r.__z2Origin=r.z2,r.z2+=x):null!=_&&(r.z2=_,r.__z2Origin=null);var w=i&&i.useNameLabel;$i(v,u,d,f,{labelFetcher:a,labelDataIndex:e,defaultText:function(e,n){return w?t.getName(e):zl(t,e)},isRectText:!0,autoColor:s}),r.off("mouseover").off("mouseout").off("emphasis").off("normal"),r.hoverStyle=u,qi(r);var b=Nl(n);if(p&&a.isAnimationEnabled()){var M=function(){if(!this.incremental){var t=b[1]/b[0];this.animateTo({scale:[Math.max(1.1*b[0],b[0]+3),Math.max(1.1*b[1],b[1]+3*t)]},400,"elasticOut")}},S=function(){this.incremental||this.animateTo({scale:b},400,"elasticOut")};r.on("mouseover",M).on("mouseout",S).on("emphasis",M).on("normal",S)}},pw.fadeOut=function(t,e){var n=this.childAt(0);this.silent=n.silent=!0,!(e&&e.keepLabel)&&(n.style.text=null),ar(n,{style:{opacity:0},scale:[0,0]},this._seriesModel,this.dataIndex,t)},u(El,Sg);var _w=Bl.prototype;_w.updateData=function(t,e){e=Fl(e);var n=this.group,i=t.hostModel,r=this._data,o=this._symbolCtor,a=Hl(t);r||n.removeAll(),t.diff(r).add(function(i){var r=t.getItemLayout(i);if(Vl(t,r,i,e)){var s=new o(t,i,a);s.attr("position",r),t.setItemGraphicEl(i,s),n.add(s)}}).update(function(s,l){var u=r.getItemGraphicEl(l),h=t.getItemLayout(s);Vl(t,h,s,e)?(u?(u.updateData(t,s,a),ar(u,{position:h},i)):(u=new o(t,s)).attr("position",h),n.add(u),t.setItemGraphicEl(s,u)):n.remove(u)}).remove(function(t){var e=r.getItemGraphicEl(t);e&&e.fadeOut(function(){n.remove(e)})}).execute(),this._data=t},_w.isPersistent=function(){return!0},_w.updateLayout=function(){var t=this._data;t&&t.eachItemGraphicEl(function(e,n){var i=t.getItemLayout(n);e.attr("position",i)})},_w.incrementalPrepareUpdate=function(t){this._seriesScope=Hl(t),this._data=null,this.group.removeAll()},_w.incrementalUpdate=function(t,e,n){n=Fl(n);for(var i=t.start;i0&&Xl(n[r-1]);r--);for(;i0&&Xl(n[o-1]);o--);for(;r=0){var a=r.getItemGraphicEl(o);if(!a){var s=r.getItemLayout(o);if(!s)return;(a=new El(r,o)).position=s,a.setZ(t.get("zlevel"),t.get("z")),a.ignore=isNaN(s[0])||isNaN(s[1]),a.__temp=!0,r.setItemGraphicEl(o,a),a.stopSymbolAnimation(!0),this.group.add(a)}a.highlight()}else ra.prototype.highlight.call(this,t,e,n,i)},downplay:function(t,e,n,i){var r=t.getData(),o=Tn(r,i);if(null!=o&&o>=0){var a=r.getItemGraphicEl(o);a&&(a.__temp?(r.setItemGraphicEl(o,null),this.group.remove(a)):a.downplay())}else ra.prototype.downplay.call(this,t,e,n,i)},_newPolyline:function(t){var e=this._polyline;return e&&this._lineGroup.remove(e),e=new Aw({shape:{points:t},silent:!0,z2:10}),this._lineGroup.add(e),this._polyline=e,e},_newPolygon:function(t,e){var n=this._polygon;return n&&this._lineGroup.remove(n),n=new kw({shape:{points:t,stackedOnPoints:e},silent:!0}),this._lineGroup.add(n),this._polygon=n,n},_updateAnimation:function(t,e,n,i,r,o){var a=this._polyline,s=this._polygon,l=t.hostModel,u=ww(this._data,t,this._stackedOnPoints,e,this._coordSys,n,this._valueOrigin,o),h=u.current,c=u.stackedOnCurrent,d=u.next,f=u.stackedOnNext;r&&(h=ru(u.current,n,r),c=ru(u.stackedOnCurrent,n,r),d=ru(u.next,n,r),f=ru(u.stackedOnNext,n,r)),a.shape.__points=u.current,a.shape.points=h,ar(a,{shape:{points:d}},l),s&&(s.setShape({points:h,stackedOnPoints:c}),ar(s,{shape:{points:d,stackedOnPoints:f}},l));for(var p=[],g=u.status,m=0;me&&(e=t[n]);return isFinite(e)?e:NaN},min:function(t){for(var e=1/0,n=0;ne[1]&&e.reverse(),e},getOtherAxis:function(){this.grid.getOtherAxis()},pointToData:function(t,e){return this.coordToData(this.toLocalCoord(t["x"===this.dim?0:1]),e)},toLocalCoord:null,toGlobalCoord:null},u(Nw,cw);var Rw={show:!0,zlevel:0,z:0,inverse:!1,name:"",nameLocation:"end",nameRotate:null,nameTruncate:{maxWidth:null,ellipsis:"...",placeholder:"."},nameTextStyle:{},nameGap:15,silent:!1,triggerEvent:!1,tooltip:{show:!1},axisPointer:{},axisLine:{show:!0,onZero:!0,onZeroAxisIndex:null,lineStyle:{color:"#333",width:1,type:"solid"},symbol:["none","none"],symbolSize:[10,15]},axisTick:{show:!0,inside:!1,length:5,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,showMinLabel:null,showMaxLabel:null,margin:8,fontSize:12},splitLine:{show:!0,lineStyle:{color:["#ccc"],width:1,type:"solid"}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.3)","rgba(200,200,200,0.3)"]}}},Bw={};Bw.categoryAxis=i({boundaryGap:!0,deduplication:null,splitLine:{show:!1},axisTick:{alignWithLabel:!1,interval:"auto"},axisLabel:{interval:"auto"}},Rw),Bw.valueAxis=i({boundaryGap:[0,0],splitNumber:5},Rw),Bw.timeAxis=a({scale:!0,min:"dataMin",max:"dataMax"},Bw.valueAxis),Bw.logAxis=a({scale:!0,logBase:10},Bw.valueAxis);var Vw=["value","category","time","log"],Fw=function(t,e,n,o){d(Vw,function(a){e.extend({type:t+"Axis."+a,mergeDefaultAndTheme:function(e,r){var o=this.layoutMode,s=o?Ur(e):{};i(e,r.getTheme().get(a+"Axis")),i(e,this.getDefaultOption()),e.type=n(t,e),o&&Zr(e,s,o)},optionUpdated:function(){"category"===this.option.type&&(this.__ordinalMeta=Rs.createByAxisModel(this))},getCategories:function(t){var e=this.option;if("category"===e.type)return t?e.data:this.__ordinalMeta.categories},getOrdinalMeta:function(){return this.__ordinalMeta},defaultOption:r([{},Bw[a+"Axis"],o],!0)})}),Iy.registerSubTypeDefaulter(t+"Axis",v(n,t))},Hw=Iy.extend({type:"cartesian2dAxis",axis:null,init:function(){Hw.superApply(this,"init",arguments),this.resetRange()},mergeOption:function(){Hw.superApply(this,"mergeOption",arguments),this.resetRange()},restoreData:function(){Hw.superApply(this,"restoreData",arguments),this.resetRange()},getCoordSysModel:function(){return this.ecModel.queryComponents({mainType:"grid",index:this.option.gridIndex,id:this.option.gridId})[0]}});i(Hw.prototype,K_);var Gw={offset:0};Fw("x",Hw,hu,Gw),Fw("y",Hw,hu,Gw),Iy.extend({type:"grid",dependencies:["xAxis","yAxis"],layoutMode:"box",coordinateSystem:null,defaultOption:{show:!1,zlevel:0,z:0,left:"10%",top:60,right:"10%",bottom:60,containLabel:!1,backgroundColor:"rgba(0,0,0,0)",borderWidth:1,borderColor:"#ccc"}});var Ww=du.prototype;Ww.type="grid",Ww.axisPointerEnabled=!0,Ww.getRect=function(){return this._rect},Ww.update=function(t,e){var n=this._axesMap;this._updateScale(t,this.model),d(n.x,function(t){il(t.scale,t.model)}),d(n.y,function(t){il(t.scale,t.model)}),d(n.x,function(t){fu(n,"y",t)}),d(n.y,function(t){fu(n,"x",t)}),this.resize(this.model,e)},Ww.resize=function(t,e,n){function i(){d(o,function(t){var e=t.isHorizontal(),n=e?[0,r.width]:[0,r.height],i=t.inverse?1:0;t.setExtent(n[i],n[1-i]),gu(t,e?r.x:r.y)})}var r=Gr(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()});this._rect=r;var o=this._axesList;i(),!n&&t.get("containLabel")&&(d(o,function(t){if(!t.model.get("axisLabel.inside")){var e=ll(t);if(e){var n=t.isHorizontal()?"height":"width",i=t.model.get("axisLabel.margin");r[n]-=e[n]+i,"top"===t.position?r.y+=e.height+i:"left"===t.position&&(r.x+=e.width+i)}}}),i())},Ww.getAxis=function(t,e){var n=this._axesMap[t];if(null!=n){if(null==e)for(var i in n)if(n.hasOwnProperty(i))return n[i];return n[e]}},Ww.getAxes=function(){return this._axesList.slice()},Ww.getCartesian=function(t,e){if(null!=t&&null!=e){var n="x"+t+"y"+e;return this._coordsMap[n]}w(t)&&(e=t.yAxisIndex,t=t.xAxisIndex);for(var i=0,r=this._coordsList;iu[1]?-1:1,c=["start"===r?u[0]-h*l:"end"===r?u[1]+h*l:(u[0]+u[1])/2,Su(r)?t.labelOffset+a*l:0],d=e.get("nameRotate");null!=d&&(d=d*Uw/180);var f;Su(r)?i=Yw(t.rotation,null!=d?d:t.rotation,a):(i=xu(t,r,d||0,u),null!=(f=t.axisNameAvailableWidth)&&(f=Math.abs(f/Math.sin(i.rotation)),!isFinite(f)&&(f=null)));var p=s.getFont(),g=e.get("nameTruncate",!0)||{},m=g.ellipsis,v=C(t.nameTruncateMaxWidth,g.maxWidth,f),y=null!=m&&null!=v?my(n,v,p,m,{minChar:2,placeholder:g.placeholder}):n,x=e.get("tooltip",!0),_=e.mainType,w={componentType:_,name:n,$vars:["name"]};w[_+"Index"]=e.componentIndex;var b=new kv({anid:"name",__fullText:n,__truncatedText:y,position:c,rotation:i.rotation,silent:_u(e),z2:1,tooltip:x&&x.show?o({content:n,formatter:function(){return n},formatterParams:w},x):null});Ki(b.style,s,{text:y,textFont:p,textFill:s.getTextColor()||e.get("axisLine.lineStyle.color"),textAlign:i.textAlign,textVerticalAlign:i.textVerticalAlign}),e.get("triggerEvent")&&(b.eventData=yu(e),b.eventData.targetType="axisName",b.eventData.name=n),this._dumbGroup.add(b),b.updateTransform(),this.group.add(b),b.decomposeTransform()}}},Yw=Xw.innerTextLayout=function(t,e,n){var i,r,o=Tr(e-t);return Dr(o)?(r=n>0?"top":"bottom",i="center"):Dr(o-Uw)?(r=n>0?"bottom":"top",i="center"):(r="middle",i=o>0&&o0?"right":"left":n>0?"left":"right"),{rotation:o,textAlign:i,textVerticalAlign:r}},qw=d,$w=v,Kw=as({type:"axis",_axisPointer:null,axisPointerClass:null,render:function(t,e,n,i){this.axisPointerClass&&Ou(t),Kw.superApply(this,"render",arguments),Bu(this,t,0,n,0,!0)},updateAxisPointer:function(t,e,n,i,r){Bu(this,t,0,n,0,!1)},remove:function(t,e){var n=this._axisPointer;n&&n.remove(e),Kw.superApply(this,"remove",arguments)},dispose:function(t,e){Vu(this,e),Kw.superApply(this,"dispose",arguments)}}),Qw=[];Kw.registerAxisPointerClass=function(t,e){Qw[t]=e},Kw.getAxisPointerClass=function(t){return t&&Qw[t]};var Jw=["axisLine","axisTickLabel","axisName"],tb=["splitArea","splitLine"],eb=Kw.extend({type:"cartesianAxis",axisPointerClass:"CartesianAxisPointer",render:function(t,e,n,i){this.group.removeAll();var r=this._axisGroup;if(this._axisGroup=new Sg,this.group.add(this._axisGroup),t.get("show")){var o=t.getCoordSysModel(),a=Fu(o,t),s=new Xw(t,a);d(Jw,s.add,s),this._axisGroup.add(s.getGroup()),d(tb,function(e){t.get(e+".show")&&this["_"+e](t,o)},this),cr(r,this._axisGroup,t),eb.superCall(this,"render",t,e,n,i)}},remove:function(){this._splitAreaColors=null},_splitLine:function(t,e){var n=t.axis;if(!n.scale.isBlank()){var i=t.getModel("splitLine"),r=i.getModel("lineStyle"),o=r.get("color");o=y(o)?o:[o];for(var s=e.coordinateSystem.getRect(),l=n.isHorizontal(),u=0,h=n.getTicksCoords({tickModel:i}),c=[],d=[],f=r.getLineStyle(),p=0;p1){var c;"string"==typeof r?c=Ow[r]:"function"==typeof r&&(c=r),c&&t.setData(i.downSample(i.mapDimension(s.dim),1/h,c,zw))}}}}}("line")),cx.extend({type:"series.__base_bar__",getInitialData:function(t,e){return Os(this.getSource(),this)},getMarkerPosition:function(t){var e=this.coordinateSystem;if(e){var n=e.dataToPoint(e.clampData(t)),i=this.getData(),r=i.getLayout("offset"),o=i.getLayout("size");return n[e.getBaseAxis().isHorizontal()?0:1]+=r+o/2,n}return[NaN,NaN]},defaultOption:{zlevel:0,z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,barMinHeight:0,barMinAngle:0,large:!1,largeThreshold:400,progressive:3e3,progressiveChunkMode:"mod",itemStyle:{},emphasis:{}}}).extend({type:"series.bar",dependencies:["grid","polar"],brushSelector:"rect",getProgressive:function(){return!!this.get("large")&&this.get("progressive")},getProgressiveThreshold:function(){var t=this.get("progressiveThreshold"),e=this.get("largeThreshold");return e>t&&(t=e),t}});var nb=Sm([["fill","color"],["stroke","borderColor"],["lineWidth","borderWidth"],["stroke","barBorderColor"],["lineWidth","barBorderWidth"],["opacity"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["shadowColor"]]),ib={getBarItemStyle:function(t){var e=nb(this,t);if(this.getBorderLineDash){var n=this.getBorderLineDash();n&&(e.lineDash=n)}return e}},rb=["itemStyle","barBorderWidth"];o(pr.prototype,ib),ls({type:"bar",render:function(t,e,n){this._updateDrawMode(t);var i=t.get("coordinateSystem");return"cartesian2d"!==i&&"polar"!==i||(this._isLargeDraw?this._renderLarge(t,e,n):this._renderNormal(t,e,n)),this.group},incrementalPrepareRender:function(t,e,n){this._clear(),this._updateDrawMode(t)},incrementalRender:function(t,e,n,i){this._incrementalRenderLarge(t,e)},_updateDrawMode:function(t){var e=t.pipelineContext.large;(null==this._isLargeDraw||e^this._isLargeDraw)&&(this._isLargeDraw=e,this._clear())},_renderNormal:function(t,e,n){var i,r=this.group,o=t.getData(),a=this._data,s=t.coordinateSystem,l=s.getBaseAxis();"cartesian2d"===s.type?i=l.isHorizontal():"polar"===s.type&&(i="angle"===l.dim);var u=t.isAnimationEnabled()?t:null;o.diff(a).add(function(e){if(o.hasValue(e)){var n=o.getItemModel(e),a=ab[s.type](o,e,n),l=ob[s.type](o,e,n,a,i,u);o.setItemGraphicEl(e,l),r.add(l),Uu(l,o,e,n,a,t,i,"polar"===s.type)}}).update(function(e,n){var l=a.getItemGraphicEl(n);if(o.hasValue(e)){var h=o.getItemModel(e),c=ab[s.type](o,e,h);l?ar(l,{shape:c},u,e):l=ob[s.type](o,e,h,c,i,u,!0),o.setItemGraphicEl(e,l),r.add(l),Uu(l,o,e,h,c,t,i,"polar"===s.type)}else r.remove(l)}).remove(function(t){var e=a.getItemGraphicEl(t);"cartesian2d"===s.type?e&&Wu(t,u,e):e&&Zu(t,u,e)}).execute(),this._data=o},_renderLarge:function(t,e,n){this._clear(),ju(t,this.group)},_incrementalRenderLarge:function(t,e){ju(e,this.group,!0)},dispose:R,remove:function(t){this._clear(t)},_clear:function(t){var e=this.group,n=this._data;t&&t.get("animation")&&n&&!this._isLargeDraw?n.eachItemGraphicEl(function(e){"sector"===e.type?Zu(e.dataIndex,t,e):Wu(e.dataIndex,t,e)}):e.removeAll(),this._data=null}});var ob={cartesian2d:function(t,e,n,i,r,a,s){var l=new Fv({shape:o({},i)});if(a){var u=l.shape,h=r?"height":"width",c={};u[h]=0,c[h]=i[h],ty[s?"updateProps":"initProps"](l,{shape:c},a,e)}return l},polar:function(t,e,n,i,r,o,s){var l=i.startAngle0?1:-1,a=i.height>0?1:-1;return{x:i.x+o*r/2,y:i.y+a*r/2,width:i.width-o*r,height:i.height-a*r}},polar:function(t,e,n){var i=t.getItemLayout(e);return{cx:i.cx,cy:i.cy,r0:i.r0,r:i.r,startAngle:i.startAngle,endAngle:i.endAngle}}},sb=xi.extend({type:"largeBar",shape:{points:[]},buildPath:function(t,e){for(var n=e.points,i=this.__startPoint,r=this.__valueIdx,o=0;o=0?"p":"n",b=m;p&&(r[a][_]||(r[a][_]={p:m,n:m}),b=r[a][_][w]);var M,S,I,C;if(g)M=b,S=(T=n.dataToPoint([x,_]))[1]+l,I=T[0]-m,C=u,Math.abs(I)0&&"scale"!==u){var d=r.getItemLayout(0),f=Math.max(n.getWidth(),n.getHeight())/2,p=m(a.removeClipPath,a);a.setClipPath(this._createClipPath(d.cx,d.cy,f,d.startAngle,d.clockwise,p,t))}this._data=r}},dispose:function(){},_createClipPath:function(t,e,n,i,r,o,a){var s=new zv({shape:{cx:t,cy:e,r0:0,r:n,startAngle:i,endAngle:i,clockwise:r}});return sr(s,{shape:{endAngle:i+(r?1:-1)*Math.PI*2}},a,o),s},containPoint:function(t,e){var n=e.getData().getItemLayout(0);if(n){var i=t[0]-n.cx,r=t[1]-n.cy,o=Math.sqrt(i*i+r*r);return o<=n.r&&o>=n.r0}}});var db=function(t,e,n,i){var r,o,a=t.getData(),s=[],l=!1;a.each(function(n){var i,u,h,c,d=a.getItemLayout(n),f=a.getItemModel(n),p=f.getModel("label"),g=p.get("position")||f.get("emphasis.label.position"),m=f.getModel("labelLine"),v=m.get("length"),y=m.get("length2"),x=(d.startAngle+d.endAngle)/2,_=Math.cos(x),w=Math.sin(x);r=d.cx,o=d.cy;var b="inside"===g||"inner"===g;if("center"===g)i=d.cx,u=d.cy,c="center";else{var M=(b?(d.r+d.r0)/2*_:d.r*_)+r,S=(b?(d.r+d.r0)/2*w:d.r*w)+o;if(i=M+3*_,u=S+3*w,!b){var I=M+_*(v+e-d.r),C=S+w*(v+e-d.r),T=I+(_<0?-1:1)*y,D=C;i=T+(_<0?-5:5),u=D,h=[[M,S],[I,C],[T,D]]}c=b?"center":_>0?"left":"right"}var A=p.getFont(),k=p.get("rotate")?_<0?-x+Math.PI:-x:0,P=ce(t.getFormattedLabel(n,"normal")||a.getName(n),A,c,"top");l=!!k,d.label={x:i,y:u,position:g,height:P.height,len:v,len2:y,linePoints:h,textAlign:c,verticalAlign:"middle",rotation:k,inside:b},b||s.push(d.label)}),!l&&t.get("avoidLabelOverlap")&&Ju(s,r,o,e,n,i)},fb=2*Math.PI,pb=Math.PI/180;!function(t,e){d(e,function(e){e.update="updateView",ts(e,function(n,i){var r={};return i.eachComponent({mainType:"series",subType:t,query:n},function(t){t[e.method]&&t[e.method](n.name,n.dataIndex);var i=t.getData();i.each(function(e){var n=i.getName(e);r[n]=t.isSelected(n)||!1})}),{name:n.name,selected:r}})})}("pie",[{type:"pieToggleSelect",event:"pieselectchanged",method:"toggleSelected"},{type:"pieSelect",event:"pieselected",method:"select"},{type:"pieUnSelect",event:"pieunselected",method:"unSelect"}]),ns(function(t){return{getTargetSeries:function(e){var n={},i=N();return e.eachSeriesByType(t,function(t){t.__paletteScope=n,i.set(t.uid,t)}),i},reset:function(t,e){var n=t.getRawData(),i={},r=t.getData();r.each(function(t){var e=r.getRawIndex(t);i[e]=t}),n.each(function(e){var o=i[e],a=null!=o&&r.getItemVisual(o,"color",!0);if(a)n.setItemVisual(e,"color",a);else{var s=n.getItemModel(e).get("itemStyle.color")||t.getColorFromPalette(n.getName(e)||e+"",t.__paletteScope,n.count());n.setItemVisual(e,"color",s),null!=o&&r.setItemVisual(o,"color",s)}})}}}("pie")),es(v(function(t,e,n,i){e.eachSeriesByType(t,function(t){var e=t.getData(),i=e.mapDimension("value"),r=t.get("center"),o=t.get("radius");y(o)||(o=[0,o]),y(r)||(r=[r,r]);var a=n.getWidth(),s=n.getHeight(),l=Math.min(a,s),u=_r(r[0],a),h=_r(r[1],s),c=_r(o[0],l/2),d=_r(o[1],l/2),f=-t.get("startAngle")*pb,p=t.get("minAngle")*pb,g=0;e.each(i,function(t){!isNaN(t)&&g++});var m=e.getSum(i),v=Math.PI/(m||g)*2,x=t.get("clockwise"),_=t.get("roseType"),w=t.get("stillShowZeroSum"),b=e.getDataExtent(i);b[0]=0;var M=fb,S=0,I=f,C=x?1:-1;if(e.each(i,function(t,n){var i;if(isNaN(t))e.setItemLayout(n,{angle:NaN,startAngle:NaN,endAngle:NaN,clockwise:x,cx:u,cy:h,r0:c,r:_?NaN:d});else{(i="area"!==_?0===m&&w?v:t*v:fb/g)=0;s--){var l=2*s,u=i[l]-o/2,h=i[l+1]-a/2;if(t>=u&&e>=h&&t<=u+o&&e<=h+a)return s}return-1}}),mb=th.prototype;mb.isPersistent=function(){return!this._incremental},mb.updateData=function(t){this.group.removeAll();var e=new gb({rectHover:!0,cursor:"default"});e.setShape({points:t.getLayout("symbolPoints")}),this._setCommon(e,t),this.group.add(e),this._incremental=null},mb.updateLayout=function(t){if(!this._incremental){var e=t.getLayout("symbolPoints");this.group.eachChild(function(t){if(null!=t.startIndex){var n=2*(t.endIndex-t.startIndex),i=4*t.startIndex*2;e=new Float32Array(e.buffer,i,n)}t.setShape("points",e)})}},mb.incrementalPrepareUpdate=function(t){this.group.removeAll(),this._clearIncremental(),t.count()>2e6?(this._incremental||(this._incremental=new Di({silent:!0})),this.group.add(this._incremental)):this._incremental=null},mb.incrementalUpdate=function(t,e){var n;this._incremental?(n=new gb,this._incremental.addDisplayable(n,!0)):((n=new gb({rectHover:!0,cursor:"default",startIndex:t.start,endIndex:t.end})).incremental=!0,this.group.add(n)),n.setShape({points:e.getLayout("symbolPoints")}),this._setCommon(n,e,!!this._incremental)},mb._setCommon=function(t,e,n){var i=e.hostModel,r=e.getVisual("symbolSize");t.setShape("size",r instanceof Array?r:[r,r]),t.symbolProxy=cl(e.getVisual("symbol"),0,0,0,0),t.setColor=t.symbolProxy.setColor;var o=t.shape.size[0]<4;t.useStyle(i.getModel("itemStyle").getItemStyle(o?["color","shadowBlur","shadowColor"]:["color"]));var a=e.getVisual("color");a&&t.setColor(a),n||(t.seriesIndex=i.seriesIndex,t.on("mousemove",function(e){t.dataIndex=null;var n=t.findDataIndex(e.offsetX,e.offsetY);n>=0&&(t.dataIndex=n+(t.startIndex||0))}))},mb.remove=function(){this._clearIncremental(),this._incremental=null,this.group.removeAll()},mb._clearIncremental=function(){var t=this._incremental;t&&t.clearDisplaybles()},ls({type:"scatter",render:function(t,e,n){var i=t.getData();this._updateSymbolDraw(i,t).updateData(i),this._finished=!0},incrementalPrepareRender:function(t,e,n){var i=t.getData();this._updateSymbolDraw(i,t).incrementalPrepareUpdate(i),this._finished=!1},incrementalRender:function(t,e,n){this._symbolDraw.incrementalUpdate(t,e.getData()),this._finished=t.end===e.getData().count()},updateTransform:function(t,e,n){var i=t.getData();if(this.group.dirty(),!this._finished||i.count()>1e4||!this._symbolDraw.isPersistent())return{update:!0};var r=Lw().reset(t);r.progress&&r.progress({start:0,end:i.count()},i),this._symbolDraw.updateLayout(i)},_updateSymbolDraw:function(t,e){var n=this._symbolDraw,i=e.pipelineContext.large;return n&&i===this._isLargeDraw||(n&&n.remove(),n=this._symbolDraw=i?new th:new Bl,this._isLargeDraw=i,this.group.removeAll()),this.group.add(n.group),n},remove:function(t,e){this._symbolDraw&&this._symbolDraw.remove(!0),this._symbolDraw=null},dispose:function(){}}),ns(Pw("scatter","circle")),es(Lw("scatter")),Qa(function(t){var e=t.graphic;y(e)?e[0]&&e[0].elements?t.graphic=[t.graphic[0]]:t.graphic=[{elements:e}]:e&&!e.elements&&(t.graphic=[{elements:[e]}])});var vb=os({type:"graphic",defaultOption:{elements:[],parentId:null},_elOptionsToUpdate:null,mergeOption:function(t){var e=this.option.elements;this.option.elements=null,vb.superApply(this,"mergeOption",arguments),this.option.elements=e},optionUpdated:function(t,e){var n=this.option,i=(e?n:t).elements,r=n.elements=e?[]:n.elements,o=[];this._flatten(i,o);var a=Mn(r,o);Sn(a);var s=this._elOptionsToUpdate=[];d(a,function(t,e){var n=t.option;n&&(s.push(n),oh(t,n),ah(r,e,n),sh(r[e],n))},this);for(var l=r.length-1;l>=0;l--)null==r[l]?r.splice(l,1):delete r[l].$action},_flatten:function(t,e,n){d(t,function(t){if(t){n&&(t.parentOption=n),e.push(t);var i=t.children;"group"===t.type&&i&&this._flatten(i,e,t),delete t.children}},this)},useElOptionsToUpdate:function(){var t=this._elOptionsToUpdate;return this._elOptionsToUpdate=null,t}});as({type:"graphic",init:function(t,e){this._elMap=N(),this._lastGraphicModel},render:function(t,e,n){t!==this._lastGraphicModel&&this._clear(),this._lastGraphicModel=t,this._updateElements(t,n),this._relocate(t,n)},_updateElements:function(t,e){var n=t.useElOptionsToUpdate();if(n){var i=this._elMap,r=this.group;d(n,function(t){var e=t.$action,n=t.id,o=i.get(n),a=t.parentId,s=null!=a?i.get(a):r;if("text"===t.type){var l=t.style;t.hv&&t.hv[1]&&(l.textVerticalAlign=l.textBaseline=null),!l.hasOwnProperty("textFill")&&l.fill&&(l.textFill=l.fill),!l.hasOwnProperty("textStroke")&&l.stroke&&(l.textStroke=l.stroke)}var u=ih(t);e&&"merge"!==e?"replace"===e?(nh(o,i),eh(n,s,u,i)):"remove"===e&&nh(o,i):o?o.attr(u):eh(n,s,u,i);var h=i.get(n);h&&(h.__ecGraphicWidth=t.width,h.__ecGraphicHeight=t.height)})}},_relocate:function(t,e){for(var n=t.option.elements,i=this.group,r=this._elMap,o=n.length-1;o>=0;o--){var a=n[o],s=r.get(a.id);if(s){var l=s.parent;Wr(s,a,l===i?{width:e.getWidth(),height:e.getHeight()}:{width:l.__ecGraphicWidth||0,height:l.__ecGraphicHeight||0},null,{hv:a.hv,boundingMode:a.bounding})}}},_clear:function(){var t=this._elMap;t.each(function(e){nh(e,t)}),this._elMap=N()},dispose:function(){this._clear()}});var yb=function(t,e){var n,i=[],r=t.seriesIndex;if(null==r||!(n=e.getSeriesByIndex(r)))return{point:[]};var o=n.getData(),a=Tn(o,t);if(null==a||a<0||y(a))return{point:[]};var s=o.getItemGraphicEl(a),l=n.coordinateSystem;if(n.getTooltipPosition)i=n.getTooltipPosition(a)||[];else if(l&&l.dataToPoint)i=l.dataToPoint(o.getValues(f(l.dimensions,function(t){return o.mapDimension(t)}),a,!0))||[];else if(s){var u=s.getBoundingRect().clone();u.applyTransform(s.transform),i=[u.x+u.width/2,u.y+u.height/2]}return{point:i,el:s}},xb=d,_b=v,wb=Dn(),bb=(os({type:"axisPointer",coordSysAxesInfo:null,defaultOption:{show:"auto",triggerOn:null,zlevel:0,z:50,type:"line",snap:!1,triggerTooltip:!0,value:null,status:null,link:[],animation:null,animationDurationUpdate:200,lineStyle:{color:"#aaa",width:1,type:"solid"},shadowStyle:{color:"rgba(150,150,150,0.3)"},label:{show:!0,formatter:null,precision:"auto",margin:3,color:"#fff",padding:[5,7,5,7],backgroundColor:"auto",borderColor:null,borderWidth:0,shadowBlur:3,shadowColor:"#aaa"},handle:{show:!1,icon:"M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z",size:45,margin:50,color:"#333",shadowBlur:3,shadowColor:"#aaa",shadowOffsetX:0,shadowOffsetY:2,throttle:40}}}),Dn()),Mb=d,Sb=as({type:"axisPointer",render:function(t,e,n){var i=e.getComponent("tooltip"),r=t.get("triggerOn")||i&&i.get("triggerOn")||"mousemove|click";yh("axisPointer",n,function(t,e,n){"none"!==r&&("leave"===t||r.indexOf(t)>=0)&&n({type:"updateAxisPointer",currTrigger:t,x:e&&e.offsetX,y:e&&e.offsetY})})},remove:function(t,e){Sh(e.getZr(),"axisPointer"),Sb.superApply(this._model,"remove",arguments)},dispose:function(t,e){Sh("axisPointer",e),Sb.superApply(this._model,"dispose",arguments)}}),Ib=Dn(),Cb=n,Tb=m;(Ih.prototype={_group:null,_lastGraphicKey:null,_handle:null,_dragging:!1,_lastValue:null,_lastStatus:null,_payloadInfo:null,animationThreshold:15,render:function(t,e,n,i){var r=e.get("value"),o=e.get("status");if(this._axisModel=t,this._axisPointerModel=e,this._api=n,i||this._lastValue!==r||this._lastStatus!==o){this._lastValue=r,this._lastStatus=o;var a=this._group,s=this._handle;if(!o||"hide"===o)return a&&a.hide(),void(s&&s.hide());a&&a.show(),s&&s.show();var l={};this.makeElOption(l,r,t,e,n);var u=l.graphicKey;u!==this._lastGraphicKey&&this.clear(n),this._lastGraphicKey=u;var h=this._moveAnimation=this.determineAnimation(t,e);if(a){var c=v(Ch,e,h);this.updatePointerEl(a,l,c,e),this.updateLabelEl(a,l,c,e)}else a=this._group=new Sg,this.createPointerEl(a,l,t,e),this.createLabelEl(a,l,t,e),n.getZr().add(a);kh(a,e,!0),this._renderHandle(r)}},remove:function(t){this.clear(t)},dispose:function(t){this.clear(t)},determineAnimation:function(t,e){var n=e.get("animation"),i=t.axis,r="category"===i.type,o=e.get("snap");if(!o&&!r)return!1;if("auto"===n||null==n){var a=this.animationThreshold;if(r&&i.getBandWidth()>a)return!0;if(o){var s=zu(t).seriesDataCount,l=i.getExtent();return Math.abs(l[0]-l[1])/s>a}return!1}return!0===n},makeElOption:function(t,e,n,i,r){},createPointerEl:function(t,e,n,i){var r=e.pointer;if(r){var o=Ib(t).pointerEl=new ty[r.type](Cb(e.pointer));t.add(o)}},createLabelEl:function(t,e,n,i){if(e.label){var r=Ib(t).labelEl=new Fv(Cb(e.label));t.add(r),Dh(r,i)}},updatePointerEl:function(t,e,n){var i=Ib(t).pointerEl;i&&(i.setStyle(e.pointer.style),n(i,{shape:e.pointer.shape}))},updateLabelEl:function(t,e,n,i){var r=Ib(t).labelEl;r&&(r.setStyle(e.label.style),n(r,{shape:e.label.shape,position:e.label.position}),Dh(r,i))},_renderHandle:function(t){if(!this._dragging&&this.updateHandleTransform){var e=this._axisPointerModel,n=this._api.getZr(),i=this._handle,r=e.getModel("handle"),o=e.get("status");if(!r.get("show")||!o||"hide"===o)return i&&n.remove(i),void(this._handle=null);var a;this._handle||(a=!0,i=this._handle=fr(r.get("icon"),{cursor:"move",draggable:!0,onmousemove:function(t){tm(t.event)},onmousedown:Tb(this._onHandleDragMove,this,0,0),drift:Tb(this._onHandleDragMove,this),ondragend:Tb(this._onHandleDragEnd,this)}),n.add(i)),kh(i,e,!1);var s=["color","borderColor","borderWidth","opacity","shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"];i.setStyle(r.getItemStyle(null,s));var l=r.get("size");y(l)||(l=[l,l]),i.attr("scale",[l[0]/2,l[1]/2]),ha(this,"_doDispatchAxisPointer",r.get("throttle")||0,"fixRate"),this._moveHandleToValue(t,a)}},_moveHandleToValue:function(t,e){Ch(this._axisPointerModel,!e&&this._moveAnimation,this._handle,Ah(this.getHandleTransform(t,this._axisModel,this._axisPointerModel)))},_onHandleDragMove:function(t,e){var n=this._handle;if(n){this._dragging=!0;var i=this.updateHandleTransform(Ah(n),[t,e],this._axisModel,this._axisPointerModel);this._payloadInfo=i,n.stopAnimation(),n.attr(Ah(i)),Ib(n).lastProp=null,this._doDispatchAxisPointer()}},_doDispatchAxisPointer:function(){if(this._handle){var t=this._payloadInfo,e=this._axisModel;this._api.dispatchAction({type:"updateAxisPointer",x:t.cursorPoint[0],y:t.cursorPoint[1],tooltipOption:t.tooltipOption,axesInfo:[{axisDim:e.axis.dim,axisIndex:e.componentIndex}]})}},_onHandleDragEnd:function(t){if(this._dragging=!1,this._handle){var e=this._axisPointerModel.get("value");this._moveHandleToValue(e),this._api.dispatchAction({type:"hideTip"})}},getHandleTransform:null,updateHandleTransform:null,clear:function(t){this._lastValue=null,this._lastStatus=null;var e=t.getZr(),n=this._group,i=this._handle;e&&n&&(this._lastGraphicKey=null,n&&e.remove(n),i&&e.remove(i),this._group=null,this._handle=null,this._payloadInfo=null)},doClear:function(){},buildLabel:function(t,e,n){return n=n||0,{x:t[n],y:t[1-n],width:e[n],height:e[1-n]}}}).constructor=Ih,En(Ih);var Db=Ih.extend({makeElOption:function(t,e,n,i,r){var o=n.axis,a=o.grid,s=i.get("type"),l=Vh(a,o).getOtherAxis(o).getGlobalExtent(),u=o.toGlobalCoord(o.dataToCoord(e,!0));if(s&&"none"!==s){var h=Ph(i),c=Ab[s](o,u,l,h);c.style=h,t.graphicKey=c.type,t.pointer=c}Nh(e,t,Fu(a.model,n),n,i,r)},getHandleTransform:function(t,e,n){var i=Fu(e.axis.grid.model,e,{labelInside:!1});return i.labelMargin=n.get("handle.margin"),{position:Eh(e.axis,t,i),rotation:i.rotation+(i.labelDirection<0?Math.PI:0)}},updateHandleTransform:function(t,e,n,i){var r=n.axis,o=r.grid,a=r.getGlobalExtent(!0),s=Vh(o,r).getOtherAxis(r).getGlobalExtent(),l="x"===r.dim?0:1,u=t.position;u[l]+=e[l],u[l]=Math.min(a[1],u[l]),u[l]=Math.max(a[0],u[l]);var h=(s[1]+s[0])/2,c=[h,h];c[l]=u[l];var d=[{verticalAlign:"middle"},{align:"center"}];return{position:u,rotation:t.rotation,cursorPoint:c,tooltipOption:d[l]}}}),Ab={line:function(t,e,n,i){var r=Rh([e,n[0]],[e,n[1]],Fh(t));return zi({shape:r,style:i}),{type:"Line",shape:r}},shadow:function(t,e,n,i){var r=Math.max(1,t.getBandWidth()),o=n[1]-n[0];return{type:"Rect",shape:Bh([e-r/2,n[0]],[r,o],Fh(t))}}};Kw.registerAxisPointerClass("CartesianAxisPointer",Db),Qa(function(t){if(t){(!t.axisPointer||0===t.axisPointer.length)&&(t.axisPointer={});var e=t.axisPointer.link;e&&!y(e)&&(t.axisPointer.link=[e])}}),Ja(Xx.PROCESSOR.STATISTIC,function(t,e){t.getComponent("axisPointer").coordSysAxesInfo=Tu(t,e)}),ts({type:"updateAxisPointer",event:"updateAxisPointer",update:":updateAxisPointer"},function(t,e,n){var i=t.currTrigger,r=[t.x,t.y],o=t,a=t.dispatchAction||m(n.dispatchAction,n),s=e.getComponent("axisPointer").coordSysAxesInfo;if(s){vh(r)&&(r=yb({seriesIndex:o.seriesIndex,dataIndex:o.dataIndex},e).point);var l=vh(r),u=o.axesInfo,h=s.axesInfo,c="leave"===i||vh(r),d={},f={},p={list:[],map:{}},g={showPointer:_b(hh,f),showTooltip:_b(ch,p)};xb(s.coordSysMap,function(t,e){var n=l||t.containPoint(r);xb(s.coordSysAxesInfo[e],function(t,e){var i=t.axis,o=gh(u,t);if(!c&&n&&(!u||o)){var a=o&&o.value;null!=a||l||(a=i.pointToData(r)),null!=a&&lh(t,a,g,!1,d)}})});var v={};return xb(h,function(t,e){var n=t.linkGroup;n&&!f[e]&&xb(n.axesInfo,function(e,i){var r=f[i];if(e!==t&&r){var o=r.value;n.mapper&&(o=t.axis.scale.parse(n.mapper(o,mh(e),mh(t)))),v[t.key]=o}})}),xb(v,function(t,e){lh(h[e],t,g,!0,d)}),dh(f,h,d),fh(p,r,t,a),ph(h,0,n),d}}),os({type:"tooltip",dependencies:["axisPointer"],defaultOption:{zlevel:0,z:8,show:!0,showContent:!0,trigger:"item",triggerOn:"mousemove|click",alwaysShowContent:!1,displayMode:"single",confine:!1,showDelay:0,hideDelay:100,transitionDuration:.4,enterable:!1,backgroundColor:"rgba(50,50,50,0.7)",borderColor:"#333",borderRadius:4,borderWidth:0,padding:5,extraCssText:"",axisPointer:{type:"line",axis:"auto",animation:"auto",animationDurationUpdate:200,animationEasingUpdate:"exponentialOut",crossStyle:{color:"#999",width:1,type:"dashed",textStyle:{}}},textStyle:{color:"#fff",fontSize:14}}});var kb=d,Pb=zr,Lb=["","-webkit-","-moz-","-o-"];Zh.prototype={constructor:Zh,_enterable:!0,update:function(){var t=this._container,e=t.currentStyle||document.defaultView.getComputedStyle(t),n=t.style;"absolute"!==n.position&&"absolute"!==e.position&&(n.position="relative")},show:function(t){clearTimeout(this._hideTimeout);var e=this.el;e.style.cssText="position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;"+Wh(t)+";left:"+this._x+"px;top:"+this._y+"px;"+(t.get("extraCssText")||""),e.style.display=e.innerHTML?"block":"none",this._show=!0},setContent:function(t){this.el.innerHTML=null==t?"":t},setEnterable:function(t){this._enterable=t},getSize:function(){var t=this.el;return[t.clientWidth,t.clientHeight]},moveTo:function(t,e){var n,i=this._zr;i&&i.painter&&(n=i.painter.getViewportRootOffset())&&(t+=n.offsetLeft,e+=n.offsetTop);var r=this.el.style;r.left=t+"px",r.top=e+"px",this._x=t,this._y=e},hide:function(){this.el.style.display="none",this._show=!1},hideLater:function(t){!this._show||this._inContent&&this._enterable||(t?(this._hideDelay=t,this._show=!1,this._hideTimeout=setTimeout(m(this.hide,this),t)):this.hide())},isShow:function(){return this._show}};var Ob=m,zb=d,Eb=_r,Nb=new Fv({shape:{x:-1,y:-1,width:2,height:2}});as({type:"tooltip",init:function(t,e){if(!bp.node){var n=new Zh(e.getDom(),e);this._tooltipContent=n}},render:function(t,e,n){if(!bp.node&&!bp.wxa){this.group.removeAll(),this._tooltipModel=t,this._ecModel=e,this._api=n,this._lastDataByCoordSys=null,this._alwaysShowContent=t.get("alwaysShowContent");var i=this._tooltipContent;i.update(),i.setEnterable(t.get("enterable")),this._initGlobalListener(),this._keepShow()}},_initGlobalListener:function(){var t=this._tooltipModel.get("triggerOn");yh("itemTooltip",this._api,Ob(function(e,n,i){"none"!==t&&(t.indexOf(e)>=0?this._tryShow(n,i):"leave"===e&&this._hide(i))},this))},_keepShow:function(){var t=this._tooltipModel,e=this._ecModel,n=this._api;if(null!=this._lastX&&null!=this._lastY&&"none"!==t.get("triggerOn")){var i=this;clearTimeout(this._refreshUpdateTimeout),this._refreshUpdateTimeout=setTimeout(function(){i.manuallyShowTip(t,e,n,{x:i._lastX,y:i._lastY})})}},manuallyShowTip:function(t,e,n,i){if(i.from!==this.uid&&!bp.node){var r=Xh(i,n);this._ticket="";var o=i.dataByCoordSys;if(i.tooltip&&null!=i.x&&null!=i.y){var a=Nb;a.position=[i.x,i.y],a.update(),a.tooltip=i.tooltip,this._tryShow({offsetX:i.x,offsetY:i.y,target:a},r)}else if(o)this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,event:{},dataByCoordSys:i.dataByCoordSys,tooltipOption:i.tooltipOption},r);else if(null!=i.seriesIndex){if(this._manuallyAxisShowTip(t,e,n,i))return;var s=yb(i,e),l=s.point[0],u=s.point[1];null!=l&&null!=u&&this._tryShow({offsetX:l,offsetY:u,position:i.position,target:s.el,event:{}},r)}else null!=i.x&&null!=i.y&&(n.dispatchAction({type:"updateAxisPointer",x:i.x,y:i.y}),this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,target:n.getZr().findHover(i.x,i.y).target,event:{}},r))}},manuallyHideTip:function(t,e,n,i){var r=this._tooltipContent;!this._alwaysShowContent&&this._tooltipModel&&r.hideLater(this._tooltipModel.get("hideDelay")),this._lastX=this._lastY=null,i.from!==this.uid&&this._hide(Xh(i,n))},_manuallyAxisShowTip:function(t,e,n,i){var r=i.seriesIndex,o=i.dataIndex,a=e.getComponent("axisPointer").coordSysAxesInfo;if(null!=r&&null!=o&&null!=a){var s=e.getSeriesByIndex(r);if(s&&"axis"===(t=Uh([s.getData().getItemModel(o),s,(s.coordinateSystem||{}).model,t])).get("trigger"))return n.dispatchAction({type:"updateAxisPointer",seriesIndex:r,dataIndex:o,position:i.position}),!0}},_tryShow:function(t,e){var n=t.target;if(this._tooltipModel){this._lastX=t.offsetX,this._lastY=t.offsetY;var i=t.dataByCoordSys;i&&i.length?this._showAxisTooltip(i,t):n&&null!=n.dataIndex?(this._lastDataByCoordSys=null,this._showSeriesItemTooltip(t,n,e)):n&&n.tooltip?(this._lastDataByCoordSys=null,this._showComponentItemTooltip(t,n,e)):(this._lastDataByCoordSys=null,this._hide(e))}},_showOrMove:function(t,e){var n=t.get("showDelay");e=m(e,this),clearTimeout(this._showTimout),n>0?this._showTimout=setTimeout(e,n):e()},_showAxisTooltip:function(t,e){var n=this._ecModel,i=this._tooltipModel,r=[e.offsetX,e.offsetY],o=[],a=[],s=Uh([e.tooltipOption,i]);zb(t,function(t){zb(t.dataByAxis,function(t){var e=n.getComponent(t.axisDim+"Axis",t.axisIndex),i=t.value,r=[];if(e&&null!=i){var s=zh(i,e.axis,n,t.seriesDataIndices,t.valueLabelOpt);d(t.seriesDataIndices,function(o){var l=n.getSeriesByIndex(o.seriesIndex),u=o.dataIndexInside,h=l&&l.getDataParams(u);h.axisDim=t.axisDim,h.axisIndex=t.axisIndex,h.axisType=t.axisType,h.axisId=t.axisId,h.axisValue=sl(e.axis,i),h.axisValueLabel=s,h&&(a.push(h),r.push(l.formatTooltip(u,!0)))});var l=s;o.push((l?Er(l)+"
":"")+r.join("
"))}})},this),o.reverse(),o=o.join("

");var l=e.position;this._showOrMove(s,function(){this._updateContentNotChangedOnAxis(t)?this._updatePosition(s,l,r[0],r[1],this._tooltipContent,a):this._showTooltipContent(s,o,a,Math.random(),r[0],r[1],l)})},_showSeriesItemTooltip:function(t,e,n){var i=this._ecModel,r=e.seriesIndex,o=i.getSeriesByIndex(r),a=e.dataModel||o,s=e.dataIndex,l=e.dataType,u=a.getData(),h=Uh([u.getItemModel(s),a,o&&(o.coordinateSystem||{}).model,this._tooltipModel]),c=h.get("trigger");if(null==c||"item"===c){var d=a.getDataParams(s,l),f=a.formatTooltip(s,!1,l),p="item_"+a.name+"_"+s;this._showOrMove(h,function(){this._showTooltipContent(h,f,d,p,t.offsetX,t.offsetY,t.position,t.target)}),n({type:"showTip",dataIndexInside:s,dataIndex:u.getRawIndex(s),seriesIndex:r,from:this.uid})}},_showComponentItemTooltip:function(t,e,n){var i=e.tooltip;if("string"==typeof i){var r=i;i={content:r,formatter:r}}var o=new pr(i,this._tooltipModel,this._ecModel),a=o.get("content"),s=Math.random();this._showOrMove(o,function(){this._showTooltipContent(o,a,o.get("formatterParams")||{},s,t.offsetX,t.offsetY,t.position,e)}),n({type:"showTip",from:this.uid})},_showTooltipContent:function(t,e,n,i,r,o,a,s){if(this._ticket="",t.get("showContent")&&t.get("show")){var l=this._tooltipContent,u=t.get("formatter");a=a||t.get("position");var h=e;if(u&&"string"==typeof u)h=Nr(u,n,!0);else if("function"==typeof u){var c=Ob(function(e,i){e===this._ticket&&(l.setContent(i),this._updatePosition(t,a,r,o,l,n,s))},this);this._ticket=i,h=u(n,i,c)}l.setContent(h),l.show(t),this._updatePosition(t,a,r,o,l,n,s)}},_updatePosition:function(t,e,n,i,r,o,a){var s=this._api.getWidth(),l=this._api.getHeight();e=e||t.get("position");var u=r.getSize(),h=t.get("align"),c=t.get("verticalAlign"),d=a&&a.getBoundingRect().clone();if(a&&d.applyTransform(a.transform),"function"==typeof e&&(e=e([n,i],o,r.el,d,{viewSize:[s,l],contentSize:u.slice()})),y(e))n=Eb(e[0],s),i=Eb(e[1],l);else if(w(e)){e.width=u[0],e.height=u[1];var f=Gr(e,{width:s,height:l});n=f.x,i=f.y,h=null,c=null}else"string"==typeof e&&a?(n=(p=$h(e,d,u))[0],i=p[1]):(n=(p=jh(n,i,r.el,s,l,h?null:20,c?null:20))[0],i=p[1]);if(h&&(n-=Kh(h)?u[0]/2:"right"===h?u[0]:0),c&&(i-=Kh(c)?u[1]/2:"bottom"===c?u[1]:0),t.get("confine")){var p=Yh(n,i,r.el,s,l);n=p[0],i=p[1]}r.moveTo(n,i)},_updateContentNotChangedOnAxis:function(t){var e=this._lastDataByCoordSys,n=!!e&&e.length===t.length;return n&&zb(e,function(e,i){var r=e.dataByAxis||{},o=(t[i]||{}).dataByAxis||[];(n&=r.length===o.length)&&zb(r,function(t,e){var i=o[e]||{},r=t.seriesDataIndices||[],a=i.seriesDataIndices||[];(n&=t.value===i.value&&t.axisType===i.axisType&&t.axisId===i.axisId&&r.length===a.length)&&zb(r,function(t,e){var i=a[e];n&=t.seriesIndex===i.seriesIndex&&t.dataIndex===i.dataIndex})})}),this._lastDataByCoordSys=t,!!n},_hide:function(t){this._lastDataByCoordSys=null,t({type:"hideTip",from:this.uid})},dispose:function(t,e){bp.node||bp.wxa||(this._tooltipContent.hide(),Sh("itemTooltip",e))}}),ts({type:"showTip",event:"showTip",update:"tooltip:manuallyShowTip"},function(){}),ts({type:"hideTip",event:"hideTip",update:"tooltip:manuallyHideTip"},function(){});var Rb=os({type:"legend.plain",dependencies:["series"],layoutMode:{type:"box",ignoreSize:!0},init:function(t,e,n){this.mergeDefaultAndTheme(t,n),t.selected=t.selected||{}},mergeOption:function(t){Rb.superCall(this,"mergeOption",t)},optionUpdated:function(){this._updateData(this.ecModel);var t=this._data;if(t[0]&&"single"===this.get("selectedMode")){for(var e=!1,n=0;n=0},defaultOption:{zlevel:0,z:4,show:!0,orient:"horizontal",left:"center",top:0,align:"auto",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemGap:10,itemWidth:25,itemHeight:14,inactiveColor:"#ccc",textStyle:{color:"#333"},selectedMode:!0,tooltip:{show:!1}}});ts("legendToggleSelect","legendselectchanged",v(Qh,"toggleSelected")),ts("legendSelect","legendselected",v(Qh,"select")),ts("legendUnSelect","legendunselected",v(Qh,"unSelect"));var Bb=v,Vb=d,Fb=Sg,Hb=as({type:"legend.plain",newlineDisabled:!1,init:function(){this.group.add(this._contentGroup=new Fb),this._backgroundEl},getContentGroup:function(){return this._contentGroup},render:function(t,e,n){if(this.resetInner(),t.get("show",!0)){var i=t.get("align");i&&"auto"!==i||(i="right"===t.get("left")&&"vertical"===t.get("orient")?"right":"left"),this.renderInner(i,t,e,n);var r=t.getBoxLayoutParams(),o={width:n.getWidth(),height:n.getHeight()},s=t.get("padding"),l=Gr(r,o,s),u=this.layoutInner(t,i,l),h=Gr(a({width:u.width,height:u.height},r),o,s);this.group.attr("position",[h.x-u.x,h.y-u.y]),this.group.add(this._backgroundEl=tc(u,t))}},resetInner:function(){this.getContentGroup().removeAll(),this._backgroundEl&&this.group.remove(this._backgroundEl)},renderInner:function(t,e,n,i){var r=this.getContentGroup(),o=N(),a=e.get("selectedMode"),s=[];n.eachRawSeries(function(t){!t.get("legendHoverLink")&&s.push(t.id)}),Vb(e.getData(),function(l,u){var h=l.get("name");if(this.newlineDisabled||""!==h&&"\n"!==h){var c=n.getSeriesByName(h)[0];if(!o.get(h))if(c){var d=c.getData(),f=d.getVisual("color");"function"==typeof f&&(f=f(c.getDataParams(0)));var p=d.getVisual("legendSymbol")||"roundRect",g=d.getVisual("symbol");this._createItem(h,u,l,e,p,g,t,f,a).on("click",Bb(ec,h,i)).on("mouseover",Bb(nc,c,null,i,s)).on("mouseout",Bb(ic,c,null,i,s)),o.set(h,!0)}else n.eachRawSeries(function(n){if(!o.get(h)&&n.legendDataProvider){var r=n.legendDataProvider(),c=r.indexOfName(h);if(c<0)return;var d=r.getItemVisual(c,"color");this._createItem(h,u,l,e,"roundRect",null,t,d,a).on("click",Bb(ec,h,i)).on("mouseover",Bb(nc,n,h,i,s)).on("mouseout",Bb(ic,n,h,i,s)),o.set(h,!0)}},this)}else r.add(new Fb({newline:!0}))},this)},_createItem:function(t,e,n,i,r,a,s,l,u){var h=i.get("itemWidth"),c=i.get("itemHeight"),d=i.get("inactiveColor"),f=i.get("symbolKeepAspect"),p=i.isSelected(t),g=new Fb,m=n.getModel("textStyle"),v=n.get("icon"),y=n.getModel("tooltip"),x=y.parentModel;if(r=v||r,g.add(cl(r,0,0,h,c,p?l:d,null==f||f)),!v&&a&&(a!==r||"none"==a)){var _=.8*c;"none"===a&&(a="circle"),g.add(cl(a,(h-_)/2,(c-_)/2,_,_,p?l:d,null==f||f))}var w="left"===s?h+5:-5,b=s,M=i.get("formatter"),S=t;"string"==typeof M&&M?S=M.replace("{name}",null!=t?t:""):"function"==typeof M&&(S=M(t)),g.add(new kv({style:Ki({},m,{text:S,x:w,y:c/2,textFill:p?m.getTextColor():d,textAlign:b,textVerticalAlign:"middle"})}));var I=new Fv({shape:g.getBoundingRect(),invisible:!0,tooltip:y.get("show")?o({content:t,formatter:x.get("formatter",!0)||function(){return t},formatterParams:{componentType:"legend",legendIndex:i.componentIndex,name:t,$vars:["name"]}},y.option):null});return g.add(I),g.eachChild(function(t){t.silent=!0}),I.silent=!u,this.getContentGroup().add(g),qi(g),g.__legendDataIndex=e,g},layoutInner:function(t,e,n){var i=this.getContentGroup();by(t.get("orient"),i,t.get("itemGap"),n.width,n.height);var r=i.getBoundingRect();return i.attr("position",[-r.x,-r.y]),this.group.getBoundingRect()}});Ja(function(t){var e=t.findComponents({mainType:"legend"});e&&e.length&&t.filterSeries(function(t){for(var n=0;nn[s],f=[-h.x,-h.y];f[a]=i.position[a];var p=[0,0],g=[-c.x,-c.y],m=T(t.get("pageButtonGap",!0),t.get("itemGap",!0));d&&("end"===t.get("pageButtonPosition",!0)?g[a]+=n[s]-c[s]:p[a]+=c[s]+m),g[1-a]+=h[l]/2-c[l]/2,i.attr("position",f),r.attr("position",p),o.attr("position",g);var v=this.group.getBoundingRect();if((v={x:0,y:0})[s]=d?n[s]:h[s],v[l]=Math.max(h[l],c[l]),v[u]=Math.min(0,c[u]+g[1-a]),r.__rectSize=n[s],d){var y={x:0,y:0};y[s]=Math.max(n[s]-c[s]-m,0),y[l]=v[l],r.setClipPath(new Fv({shape:y})),r.__rectSize=y[s]}else o.eachChild(function(t){t.attr({invisible:!0,silent:!0})});var x=this._getPageInfo(t);return null!=x.pageIndex&&ar(i,{position:x.contentPosition},!!d&&t),this._updatePageInfoView(t,x),v},_pageGo:function(t,e,n){var i=this._getPageInfo(e)[t];null!=i&&n.dispatchAction({type:"legendScroll",scrollDataIndex:i,legendId:e.id})},_updatePageInfoView:function(t,e){var n=this._controllerGroup;d(["pagePrev","pageNext"],function(i){var r=null!=e[i+"DataIndex"],o=n.childOfName(i);o&&(o.setStyle("fill",r?t.get("pageIconColor",!0):t.get("pageIconInactiveColor",!0)),o.cursor=r?"pointer":"default")});var i=n.childOfName("pageText"),r=t.get("pageFormatter"),o=e.pageIndex,a=null!=o?o+1:0,s=e.pageCount;i&&r&&i.setStyle("text",_(r)?r.replace("{current}",a).replace("{total}",s):r({current:a,total:s}))},_getPageInfo:function(t){function e(t){var e=t.getBoundingRect().clone();return e[f]+=t.position[h],e}var n,i,r,o,a=t.get("scrollDataIndex",!0),s=this.getContentGroup(),l=s.getBoundingRect(),u=this._containerGroup.__rectSize,h=t.getOrient().index,c=Zb[h],d=Zb[1-h],f=Ub[h],p=s.position.slice();this._showController?s.eachChild(function(t){t.__legendDataIndex===a&&(o=t)}):o=s.childAt(0);var g=u?Math.ceil(l[c]/u):0;if(o){var m=o.getBoundingRect(),v=o.position[h]+m[f];p[h]=-v-l[f],n=Math.floor(g*(v+m[f]+u/2)/l[c]),n=l[c]&&g?Math.max(0,Math.min(g-1,n)):-1;var y={x:0,y:0};y[c]=u,y[d]=l[d],y[f]=-p[h]-l[f];var x,_=s.children();if(s.eachChild(function(t,n){var i=e(t);i.intersect(y)&&(null==x&&(x=n),r=t.__legendDataIndex),n===_.length-1&&i[f]+i[c]<=y[f]+y[c]&&(r=null)}),null!=x){var w=e(_[x]);if(y[f]=w[f]+w[c]-y[c],x<=0&&w[f]>=y[f])i=null;else{for(;x>0&&e(_[x-1]).intersect(y);)x--;i=_[x].__legendDataIndex}}}return{contentPosition:p,pageIndex:n,pageCount:g,pagePrevDataIndex:i,pageNextDataIndex:r}}});ts("legendScroll","legendscroll",function(t,e){var n=t.scrollDataIndex;null!=n&&e.eachComponent({mainType:"legend",subType:"scroll",query:t},function(t){t.setScrollDataIndex(n)})}),os({type:"title",layoutMode:{type:"box",ignoreSize:!0},defaultOption:{zlevel:0,z:6,show:!0,text:"",target:"blank",subtext:"",subtarget:"blank",left:0,top:0,backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,padding:5,itemGap:10,textStyle:{fontSize:18,fontWeight:"bolder",color:"#333"},subtextStyle:{color:"#aaa"}}}),as({type:"title",render:function(t,e,n){if(this.group.removeAll(),t.get("show")){var i=this.group,r=t.getModel("textStyle"),o=t.getModel("subtextStyle"),a=t.get("textAlign"),s=t.get("textBaseline"),l=new kv({style:Ki({},r,{text:t.get("text"),textFill:r.getTextColor()},{disableBox:!0}),z2:10}),u=l.getBoundingRect(),h=t.get("subtext"),c=new kv({style:Ki({},o,{text:h,textFill:o.getTextColor(),y:u.height+t.get("itemGap"),textVerticalAlign:"top"},{disableBox:!0}),z2:10}),d=t.get("link"),f=t.get("sublink");l.silent=!d,c.silent=!f,d&&l.on("click",function(){window.open(d,"_"+t.get("target"))}),f&&c.on("click",function(){window.open(f,"_"+t.get("subtarget"))}),i.add(l),h&&i.add(c);var p=i.getBoundingRect(),g=t.getBoxLayoutParams();g.width=p.width,g.height=p.height;var m=Gr(g,{width:n.getWidth(),height:n.getHeight()},t.get("padding"));a||("middle"===(a=t.get("left")||t.get("right"))&&(a="center"),"right"===a?m.x+=m.width:"center"===a&&(m.x+=m.width/2)),s||("center"===(s=t.get("top")||t.get("bottom"))&&(s="middle"),"bottom"===s?m.y+=m.height:"middle"===s&&(m.y+=m.height/2),s=s||"top"),i.attr("position",[m.x,m.y]);var v={textAlign:a,textVerticalAlign:s};l.setStyle(v),c.setStyle(v),p=i.getBoundingRect();var y=m.margin,x=t.getItemStyle(["color","opacity"]);x.fill=t.get("backgroundColor");var _=new Fv({shape:{x:p.x-y[3],y:p.y-y[0],width:p.width+y[1]+y[3],height:p.height+y[0]+y[2],r:t.get("borderRadius")},style:x,silent:!0});Ei(_),i.add(_)}}});var jb=Or,Yb=Er,qb=os({type:"marker",dependencies:["series","grid","polar","geo"],init:function(t,e,n,i){this.mergeDefaultAndTheme(t,n),this.mergeOption(t,n,i.createdBySelf,!0)},isAnimationEnabled:function(){if(bp.node)return!1;var t=this.__hostSeries;return this.getShallow("animation")&&t&&t.isAnimationEnabled()},mergeOption:function(t,e,n,i){var r=this.constructor,a=this.mainType+"Model";n||e.eachSeries(function(t){var n=t.get(this.mainType,!0),s=t[a];n&&n.data?(s?s.mergeOption(n,e,!0):(i&&oc(n),d(n.data,function(t){t instanceof Array?(oc(t[0]),oc(t[1])):oc(t)}),o(s=new r(n,this,e),{mainType:this.mainType,seriesIndex:t.seriesIndex,name:t.name,createdBySelf:!0}),s.__hostSeries=t),t[a]=s):t[a]=null},this)},formatTooltip:function(t){var e=this.getData(),n=this.getRawValue(t),i=y(n)?f(n,jb).join(", "):jb(n),r=e.getName(t),o=Yb(this.name);return(null!=n||r)&&(o+="
"),r&&(o+=Yb(r),null!=n&&(o+=" : ")),null!=n&&(o+=Yb(i)),o},getData:function(){return this._data},setData:function(t){this._data=t}});h(qb,sx),qb.extend({type:"markPoint",defaultOption:{zlevel:0,z:5,symbol:"pin",symbolSize:50,tooltip:{trigger:"item"},label:{show:!0,position:"inside"},itemStyle:{borderWidth:2},emphasis:{label:{show:!0}}}});var $b=l,Kb=v,Qb={min:Kb(lc,"min"),max:Kb(lc,"max"),average:Kb(lc,"average")},Jb=as({type:"marker",init:function(){this.markerGroupMap=N()},render:function(t,e,n){var i=this.markerGroupMap;i.each(function(t){t.__keep=!1});var r=this.type+"Model";e.eachSeries(function(t){var i=t[r];i&&this.renderSeries(t,i,e,n)},this),i.each(function(t){!t.__keep&&this.group.remove(t.group)},this)},renderSeries:function(){}});Jb.extend({type:"markPoint",updateTransform:function(t,e,n){e.eachSeries(function(t){var e=t.markPointModel;e&&(gc(e.getData(),t,n),this.markerGroupMap.get(t.id).updateLayout(e))},this)},renderSeries:function(t,e,n,i){var r=t.coordinateSystem,o=t.id,a=t.getData(),s=this.markerGroupMap,l=s.get(o)||s.set(o,new Bl),u=mc(r,t,e);e.setData(u),gc(e.getData(),t,i),u.each(function(t){var n=u.getItemModel(t),i=n.getShallow("symbolSize");"function"==typeof i&&(i=i(e.getRawValue(t),e.getDataParams(t))),u.setItemVisual(t,{symbolSize:i,color:n.get("itemStyle.color")||a.getVisual("color"),symbol:n.getShallow("symbol")})}),l.updateData(u),this.group.add(l.group),u.eachItemGraphicEl(function(t){t.traverse(function(t){t.dataModel=e})}),l.__keep=!0,l.group.silent=e.get("silent")||t.get("silent")}}),Qa(function(t){t.markPoint=t.markPoint||{}}),qb.extend({type:"markLine",defaultOption:{zlevel:0,z:5,symbol:["circle","arrow"],symbolSize:[8,16],precision:2,tooltip:{trigger:"item"},label:{show:!0,position:"end"},lineStyle:{type:"dashed"},emphasis:{label:{show:!0},lineStyle:{width:3}},animationEasing:"linear"}});var tM=Hv.prototype,eM=Wv.prototype,nM=Ai({type:"ec-line",style:{stroke:"#000",fill:null},shape:{x1:0,y1:0,x2:0,y2:0,percent:1,cpx1:null,cpy1:null},buildPath:function(t,e){(vc(e)?tM:eM).buildPath(t,e)},pointAt:function(t){return vc(this.shape)?tM.pointAt.call(this,t):eM.pointAt.call(this,t)},tangentAt:function(t){var e=this.shape,n=vc(e)?[e.x2-e.x1,e.y2-e.y1]:eM.tangentAt.call(this,t);return j(n,n)}}),iM=["fromSymbol","toSymbol"],rM=bc.prototype;rM.beforeUpdate=function(){var t=this,e=t.childOfName("fromSymbol"),n=t.childOfName("toSymbol"),i=t.childOfName("label");if(e||n||!i.ignore){for(var r=1,o=this.parent;o;)o.scale&&(r/=o.scale[0]),o=o.parent;var a=t.childOfName("line");if(this.__dirty||a.__dirty){var s=a.shape.percent,l=a.pointAt(0),u=a.pointAt(s),h=W([],u,l);if(j(h,h),e&&(e.attr("position",l),c=a.tangentAt(0),e.attr("rotation",Math.PI/2-Math.atan2(c[1],c[0])),e.attr("scale",[r*s,r*s])),n){n.attr("position",u);var c=a.tangentAt(1);n.attr("rotation",-Math.PI/2-Math.atan2(c[1],c[0])),n.attr("scale",[r*s,r*s])}if(!i.ignore){i.attr("position",u);var d,f,p,g=5*r;if("end"===i.__position)d=[h[0]*g+u[0],h[1]*g+u[1]],f=h[0]>.8?"left":h[0]<-.8?"right":"center",p=h[1]>.8?"top":h[1]<-.8?"bottom":"middle";else if("middle"===i.__position){var m=s/2,v=[(c=a.tangentAt(m))[1],-c[0]],y=a.pointAt(m);v[1]>0&&(v[0]=-v[0],v[1]=-v[1]),d=[y[0]+v[0]*g,y[1]+v[1]*g],f="center",p="bottom";var x=-Math.atan2(c[1],c[0]);u[0].8?"right":h[0]<-.8?"left":"center",p=h[1]>.8?"bottom":h[1]<-.8?"top":"middle";i.attr({style:{textVerticalAlign:i.__verticalAlign||p,textAlign:i.__textAlign||f},position:d,scale:[r,r]})}}}},rM._createLine=function(t,e,n){var i=t.hostModel,r=_c(t.getItemLayout(e));r.shape.percent=0,sr(r,{shape:{percent:1}},i,e),this.add(r);var o=new kv({name:"label"});this.add(o),d(iM,function(n){var i=xc(n,t,e);this.add(i),this[yc(n)]=t.getItemVisual(e,n)},this),this._updateCommonStl(t,e,n)},rM.updateData=function(t,e,n){var i=t.hostModel,r=this.childOfName("line"),o=t.getItemLayout(e),a={shape:{}};wc(a.shape,o),ar(r,a,i,e),d(iM,function(n){var i=t.getItemVisual(e,n),r=yc(n);if(this[r]!==i){this.remove(this.childOfName(n));var o=xc(n,t,e);this.add(o)}this[r]=i},this),this._updateCommonStl(t,e,n)},rM._updateCommonStl=function(t,e,n){var i=t.hostModel,r=this.childOfName("line"),o=n&&n.lineStyle,s=n&&n.hoverLineStyle,l=n&&n.labelModel,u=n&&n.hoverLabelModel;if(!n||t.hasItemOption){var h=t.getItemModel(e);o=h.getModel("lineStyle").getLineStyle(),s=h.getModel("emphasis.lineStyle").getLineStyle(),l=h.getModel("label"),u=h.getModel("emphasis.label")}var c=t.getItemVisual(e,"color"),f=D(t.getItemVisual(e,"opacity"),o.opacity,1);r.useStyle(a({strokeNoScale:!0,fill:"none",stroke:c,opacity:f},o)),r.hoverStyle=s,d(iM,function(t){var e=this.childOfName(t);e&&(e.setColor(c),e.setStyle({opacity:f}))},this);var p,g,m=l.getShallow("show"),v=u.getShallow("show"),y=this.childOfName("label");if((m||v)&&(p=c||"#000",null==(g=i.getFormattedLabel(e,"normal",t.dataType)))){var x=i.getRawValue(e);g=null==x?t.getName(e):isFinite(x)?wr(x):x}var _=m?g:null,w=v?T(i.getFormattedLabel(e,"emphasis",t.dataType),g):null,b=y.style;null==_&&null==w||(Ki(y.style,l,{text:_},{autoColor:p}),y.__textAlign=b.textAlign,y.__verticalAlign=b.textVerticalAlign,y.__position=l.get("position")||"middle"),y.hoverStyle=null!=w?{text:w,textFill:u.getTextColor(!0),fontStyle:u.getShallow("fontStyle"),fontWeight:u.getShallow("fontWeight"),fontSize:u.getShallow("fontSize"),fontFamily:u.getShallow("fontFamily")}:{text:null},y.ignore=!m&&!v,qi(this)},rM.highlight=function(){this.trigger("emphasis")},rM.downplay=function(){this.trigger("normal")},rM.updateLayout=function(t,e){this.setLinePoints(t.getItemLayout(e))},rM.setLinePoints=function(t){var e=this.childOfName("line");wc(e.shape,t),e.dirty()},u(bc,Sg);var oM=Mc.prototype;oM.isPersistent=function(){return!0},oM.updateData=function(t){var e=this,n=e.group,i=e._lineData;e._lineData=t,i||n.removeAll();var r=Cc(t);t.diff(i).add(function(n){Sc(e,t,n,r)}).update(function(n,o){Ic(e,i,t,o,n,r)}).remove(function(t){n.remove(i.getItemGraphicEl(t))}).execute()},oM.updateLayout=function(){var t=this._lineData;t&&t.eachItemGraphicEl(function(e,n){e.updateLayout(t,n)},this)},oM.incrementalPrepareUpdate=function(t){this._seriesScope=Cc(t),this._lineData=null,this.group.removeAll()},oM.incrementalUpdate=function(t,e){for(var n=t.start;n=0&&"number"==typeof h&&(h=+h.toFixed(Math.min(m,20))),p.coord[d]=g.coord[d]=h,a=[p,g,{type:l,valueIndex:a.valueIndex,value:h}]}return a=[uc(t,a[0]),uc(t,a[1]),o({},a[2])],a[2].type=a[2].type||"",i(a[2],a[0]),i(a[2],a[1]),a};Jb.extend({type:"markLine",updateTransform:function(t,e,n){e.eachSeries(function(t){var e=t.markLineModel;if(e){var i=e.getData(),r=e.__from,o=e.__to;r.each(function(e){Lc(r,e,!0,t,n),Lc(o,e,!1,t,n)}),i.each(function(t){i.setItemLayout(t,[r.getItemLayout(t),o.getItemLayout(t)])}),this.markerGroupMap.get(t.id).updateLayout()}},this)},renderSeries:function(t,e,n,i){function r(e,n,r){var o=e.getItemModel(n);Lc(e,n,r,t,i),e.setItemVisual(n,{symbolSize:o.get("symbolSize")||g[r?0:1],symbol:o.get("symbol",!0)||p[r?0:1],color:o.get("itemStyle.color")||s.getVisual("color")})}var o=t.coordinateSystem,a=t.id,s=t.getData(),l=this.markerGroupMap,u=l.get(a)||l.set(a,new Mc);this.group.add(u.group);var h=Oc(o,t,e),c=h.from,d=h.to,f=h.line;e.__from=c,e.__to=d,e.setData(f);var p=e.get("symbol"),g=e.get("symbolSize");y(p)||(p=[p,p]),"number"==typeof g&&(g=[g,g]),h.from.each(function(t){r(c,t,!0),r(d,t,!1)}),f.each(function(t){var e=f.getItemModel(t).get("lineStyle.color");f.setItemVisual(t,{color:e||c.getItemVisual(t,"color")}),f.setItemLayout(t,[c.getItemLayout(t),d.getItemLayout(t)]),f.setItemVisual(t,{fromSymbolSize:c.getItemVisual(t,"symbolSize"),fromSymbol:c.getItemVisual(t,"symbol"),toSymbolSize:d.getItemVisual(t,"symbolSize"),toSymbol:d.getItemVisual(t,"symbol")})}),u.updateData(f),h.line.eachItemGraphicEl(function(t,n){t.traverse(function(t){t.dataModel=e})}),u.__keep=!0,u.group.silent=e.get("silent")||t.get("silent")}}),Qa(function(t){t.markLine=t.markLine||{}}),qb.extend({type:"markArea",defaultOption:{zlevel:0,z:1,tooltip:{trigger:"item"},animation:!1,label:{show:!0,position:"top"},itemStyle:{borderWidth:0},emphasis:{label:{show:!0,position:"top"}}}});var sM=function(t,e,n,i){var o=uc(t,i[0]),a=uc(t,i[1]),s=C,l=o.coord,u=a.coord;l[0]=s(l[0],-1/0),l[1]=s(l[1],-1/0),u[0]=s(u[0],1/0),u[1]=s(u[1],1/0);var h=r([{},o,a]);return h.coord=[o.coord,a.coord],h.x0=o.x,h.y0=o.y,h.x1=a.x,h.y1=a.y,h},lM=[["x0","y0"],["x1","y0"],["x1","y1"],["x0","y1"]];Jb.extend({type:"markArea",updateTransform:function(t,e,n){e.eachSeries(function(t){var e=t.markAreaModel;if(e){var i=e.getData();i.each(function(e){var r=f(lM,function(r){return Rc(i,e,r,t,n)});i.setItemLayout(e,r),i.getItemGraphicEl(e).setShape("points",r)})}},this)},renderSeries:function(t,e,n,i){var r=t.coordinateSystem,o=t.id,s=t.getData(),l=this.markerGroupMap,u=l.get(o)||l.set(o,{group:new Sg});this.group.add(u.group),u.__keep=!0;var h=Bc(r,t,e);e.setData(h),h.each(function(e){h.setItemLayout(e,f(lM,function(n){return Rc(h,e,n,t,i)})),h.setItemVisual(e,{color:s.getVisual("color")})}),h.diff(u.__data).add(function(t){var e=new Bv({shape:{points:h.getItemLayout(t)}});h.setItemGraphicEl(t,e),u.group.add(e)}).update(function(t,n){var i=u.__data.getItemGraphicEl(n);ar(i,{shape:{points:h.getItemLayout(t)}},e,t),u.group.add(i),h.setItemGraphicEl(t,i)}).remove(function(t){var e=u.__data.getItemGraphicEl(t);u.group.remove(e)}).execute(),h.eachItemGraphicEl(function(t,n){var i=h.getItemModel(n),r=i.getModel("label"),o=i.getModel("emphasis.label"),s=h.getItemVisual(n,"color");t.useStyle(a(i.getModel("itemStyle").getItemStyle(),{fill:Pt(s,.4),stroke:s})),t.hoverStyle=i.getModel("emphasis.itemStyle").getItemStyle(),$i(t.style,t.hoverStyle,r,o,{labelFetcher:e,labelDataIndex:n,defaultText:h.getName(n)||"",isRectText:!0,autoColor:s}),qi(t,{}),t.dataModel=e}),u.__data=h,u.group.silent=e.get("silent")||t.get("silent")}}),Qa(function(t){t.markArea=t.markArea||{}}),Iy.registerSubTypeDefaulter("dataZoom",function(){return"slider"});var uM=["cartesian2d","polar","singleAxis"],hM=function(t,e){var n=f(t=t.slice(),Fr),i=f(e=(e||[]).slice(),Fr);return function(r,o){d(t,function(t,a){for(var s={name:t,capital:n[a]},l=0;l=a[0]&&t<=a[1]}if(t===this._dataZoomModel){var i=this._dimName,r=this.getTargetSeriesModels(),o=t.get("filterMode"),a=this._valueWindow;"none"!==o&&cM(r,function(t){var e=t.getData(),r=e.mapDimension(i,!0);"weakFilter"===o?e.filterSelf(function(t){for(var n,i,o,s=0;sa[1];if(u&&!h&&!c)return!0;u&&(o=!0),h&&(n=!0),c&&(i=!0)}return o&&n&&i}):cM(r,function(i){if("empty"===o)t.setData(e.map(i,function(t){return n(t)?t:NaN}));else{var r={};r[i]=a,e.selectRange(r)}}),cM(r,function(t){e.setApproximateExtent(a,t)})})}}};var pM=d,gM=hM,mM=os({type:"dataZoom",dependencies:["xAxis","yAxis","zAxis","radiusAxis","angleAxis","singleAxis","series"],defaultOption:{zlevel:0,z:4,orient:null,xAxisIndex:null,yAxisIndex:null,filterMode:"filter",throttle:null,start:0,end:100,startValue:null,endValue:null,minSpan:null,maxSpan:null,minValueSpan:null,maxValueSpan:null,rangeMode:null},init:function(t,e,n){this._dataIntervalByAxis={},this._dataInfo={},this._axisProxies={},this.textStyleModel,this._autoThrottle=!0,this._rangePropMode=["percent","percent"];var i=Uc(t);this.mergeDefaultAndTheme(t,n),this.doInit(i)},mergeOption:function(t){var e=Uc(t);i(this.option,t,!0),this.doInit(e)},doInit:function(t){var e=this.option;bp.canvasSupported||(e.realtime=!1),this._setDefaultThrottle(t),Xc(this,t),pM([["start","startValue"],["end","endValue"]],function(t,n){"value"===this._rangePropMode[n]&&(e[t[0]]=null)},this),this.textStyleModel=this.getModel("textStyle"),this._resetTarget(),this._giveAxisProxies()},_giveAxisProxies:function(){var t=this._axisProxies;this.eachTargetAxis(function(e,n,i,r){var o=this.dependentModels[e.axis][n],a=o.__dzAxisProxy||(o.__dzAxisProxy=new fM(e.name,n,this,r));t[e.name+"_"+n]=a},this)},_resetTarget:function(){var t=this.option,e=this._judgeAutoMode();gM(function(e){var n=e.axisIndex;t[n]=xn(t[n])},this),"axisIndex"===e?this._autoSetAxisIndex():"orient"===e&&this._autoSetOrient()},_judgeAutoMode:function(){var t=this.option,e=!1;gM(function(n){null!=t[n.axisIndex]&&(e=!0)},this);var n=t.orient;return null==n&&e?"orient":e?void 0:(null==n&&(t.orient="horizontal"),"axisIndex")},_autoSetAxisIndex:function(){var t=!0,e=this.get("orient",!0),n=this.option,i=this.dependentModels;if(t){var r="vertical"===e?"y":"x";i[r+"Axis"].length?(n[r+"AxisIndex"]=[0],t=!1):pM(i.singleAxis,function(i){t&&i.get("orient",!0)===e&&(n.singleAxisIndex=[i.componentIndex],t=!1)})}t&&gM(function(e){if(t){var i=[],r=this.dependentModels[e.axis];if(r.length&&!i.length)for(var o=0,a=r.length;o0?100:20}},getFirstTargetAxisModel:function(){var t;return gM(function(e){if(null==t){var n=this.get(e.axisIndex);n.length&&(t=this.dependentModels[e.axis][n[0]])}},this),t},eachTargetAxis:function(t,e){var n=this.ecModel;gM(function(i){pM(this.get(i.axisIndex),function(r){t.call(e,i,r,this,n)},this)},this)},getAxisProxy:function(t,e){return this._axisProxies[t+"_"+e]},getAxisModel:function(t,e){var n=this.getAxisProxy(t,e);return n&&n.getAxisModel()},setRawRange:function(t,e){var n=this.option;pM([["start","startValue"],["end","endValue"]],function(e){null==t[e[0]]&&null==t[e[1]]||(n[e[0]]=t[e[0]],n[e[1]]=t[e[1]])},this),!e&&Xc(this,t)},getPercentRange:function(){var t=this.findRepresentativeAxisProxy();if(t)return t.getDataPercentWindow()},getValueRange:function(t,e){if(null!=t||null!=e)return this.getAxisProxy(t,e).getDataValueWindow();var n=this.findRepresentativeAxisProxy();return n?n.getDataValueWindow():void 0},findRepresentativeAxisProxy:function(t){if(t)return t.__dzAxisProxy;var e=this._axisProxies;for(var n in e)if(e.hasOwnProperty(n)&&e[n].hostedBy(this))return e[n];for(var n in e)if(e.hasOwnProperty(n)&&!e[n].hostedBy(this))return e[n]},getRangePropMode:function(){return this._rangePropMode.slice()}}),vM=dx.extend({type:"dataZoom",render:function(t,e,n,i){this.dataZoomModel=t,this.ecModel=e,this.api=n},getTargetCoordInfo:function(){function t(t,e,n,i){for(var r,o=0;oo&&(e[1-i]=e[i]+h.sign*o),e}),xM=Fv,_M=xr,wM=br,bM=m,MM=d,SM="horizontal",IM=5,CM=["line","bar","candlestick","scatter"],TM=vM.extend({type:"dataZoom.slider",init:function(t,e){this._displayables={},this._orient,this._range,this._handleEnds,this._size,this._handleWidth,this._handleHeight,this._location,this._dragging,this._dataShadowInfo,this.api=e},render:function(t,e,n,i){TM.superApply(this,"render",arguments),ha(this,"_dispatchZoomAction",this.dataZoomModel.get("throttle"),"fixRate"),this._orient=t.get("orient"),!1!==this.dataZoomModel.get("show")?(i&&"dataZoom"===i.type&&i.from===this.uid||this._buildView(),this._updateView()):this.group.removeAll()},remove:function(){TM.superApply(this,"remove",arguments),ca(this,"_dispatchZoomAction")},dispose:function(){TM.superApply(this,"dispose",arguments),ca(this,"_dispatchZoomAction")},_buildView:function(){var t=this.group;t.removeAll(),this._resetLocation(),this._resetInterval();var e=this._displayables.barGroup=new Sg;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),t.add(e),this._positionGroup()},_resetLocation:function(){var t=this.dataZoomModel,e=this.api,n=this._findCoordRect(),i={width:e.getWidth(),height:e.getHeight()},r=this._orient===SM?{right:i.width-n.x-n.width,top:i.height-30-7,width:n.width,height:30}:{right:7,top:n.y,width:30,height:n.height},o=Ur(t.option);d(["right","top","width","height"],function(t){"ph"===o[t]&&(o[t]=r[t])});var a=Gr(o,i,t.padding);this._location={x:a.x,y:a.y},this._size=[a.width,a.height],"vertical"===this._orient&&this._size.reverse()},_positionGroup:function(){var t=this.group,e=this._location,n=this._orient,i=this.dataZoomModel.getFirstTargetAxisModel(),r=i&&i.get("inverse"),o=this._displayables.barGroup,a=(this._dataShadowInfo||{}).otherAxisInverse;o.attr(n!==SM||r?n===SM&&r?{scale:a?[-1,1]:[-1,-1]}:"vertical"!==n||r?{scale:a?[-1,-1]:[-1,1],rotation:Math.PI/2}:{scale:a?[1,-1]:[1,1],rotation:Math.PI/2}:{scale:a?[1,1]:[1,-1]});var s=t.getBoundingRect([o]);t.attr("position",[e.x-s.x,e.y-s.y])},_getViewExtent:function(){return[0,this._size[0]]},_renderBackground:function(){var t=this.dataZoomModel,e=this._size,n=this._displayables.barGroup;n.add(new xM({silent:!0,shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:t.get("backgroundColor")},z2:-40})),n.add(new xM({shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:"transparent"},z2:0,onclick:m(this._onClickPanelClick,this)}))},_renderDataShadow:function(){var t=this._dataShadowInfo=this._prepareDataShadowInfo();if(t){var e=this._size,n=t.series,i=n.getRawData(),r=n.getShadowDim?n.getShadowDim():t.otherDim;if(null!=r){var o=i.getDataExtent(r),s=.3*(o[1]-o[0]);o=[o[0]-s,o[1]+s];var l,u=[0,e[1]],h=[0,e[0]],c=[[e[0],0],[0,0]],d=[],f=h[1]/(i.count()-1),p=0,g=Math.round(i.count()/e[0]);i.each([r],function(t,e){if(g>0&&e%g)p+=f;else{var n=null==t||isNaN(t)||""===t,i=n?0:_M(t,o,u,!0);n&&!l&&e?(c.push([c[c.length-1][0],0]),d.push([d[d.length-1][0],0])):!n&&l&&(c.push([p,0]),d.push([p,0])),c.push([p,i]),d.push([p,i]),p+=f,l=n}});var m=this.dataZoomModel;this._displayables.barGroup.add(new Bv({shape:{points:c},style:a({fill:m.get("dataBackgroundColor")},m.getModel("dataBackground.areaStyle").getAreaStyle()),silent:!0,z2:-20})),this._displayables.barGroup.add(new Vv({shape:{points:d},style:m.getModel("dataBackground.lineStyle").getLineStyle(),silent:!0,z2:-19}))}}},_prepareDataShadowInfo:function(){var t=this.dataZoomModel,e=t.get("showDataShadow");if(!1!==e){var n,i=this.ecModel;return t.eachTargetAxis(function(r,o){d(t.getAxisProxy(r.name,o).getTargetSeriesModels(),function(t){if(!(n||!0!==e&&l(CM,t.get("type"))<0)){var a,s=i.getComponent(r.axis,o).axis,u=qc(r.name),h=t.coordinateSystem;null!=u&&h.getOtherAxis&&(a=h.getOtherAxis(s).inverse),u=t.getData().mapDimension(u),n={thisAxis:s,series:t,thisDim:r.name,otherDim:u,otherAxisInverse:a}}},this)},this),n}},_renderHandle:function(){var t=this._displayables,e=t.handles=[],n=t.handleLabels=[],i=this._displayables.barGroup,r=this._size,o=this.dataZoomModel;i.add(t.filler=new xM({draggable:!0,cursor:$c(this._orient),drift:bM(this._onDragMove,this,"all"),onmousemove:function(t){tm(t.event)},ondragstart:bM(this._showDataInfo,this,!0),ondragend:bM(this._onDragEnd,this),onmouseover:bM(this._showDataInfo,this,!0),onmouseout:bM(this._showDataInfo,this,!1),style:{fill:o.get("fillerColor"),textPosition:"inside"}})),i.add(new xM(Ei({silent:!0,shape:{x:0,y:0,width:r[0],height:r[1]},style:{stroke:o.get("dataBackgroundColor")||o.get("borderColor"),lineWidth:1,fill:"rgba(0,0,0,0)"}}))),MM([0,1],function(t){var r=fr(o.get("handleIcon"),{cursor:$c(this._orient),draggable:!0,drift:bM(this._onDragMove,this,t),onmousemove:function(t){tm(t.event)},ondragend:bM(this._onDragEnd,this),onmouseover:bM(this._showDataInfo,this,!0),onmouseout:bM(this._showDataInfo,this,!1)},{x:-1,y:0,width:2,height:2}),a=r.getBoundingRect();this._handleHeight=_r(o.get("handleSize"),this._size[1]),this._handleWidth=a.width/a.height*this._handleHeight,r.setStyle(o.getModel("handleStyle").getItemStyle());var s=o.get("handleColor");null!=s&&(r.style.fill=s),i.add(e[t]=r);var l=o.textStyleModel;this.group.add(n[t]=new kv({silent:!0,invisible:!0,style:{x:0,y:0,text:"",textVerticalAlign:"middle",textAlign:"center",textFill:l.getTextColor(),textFont:l.getFont()},z2:10}))},this)},_resetInterval:function(){var t=this._range=this.dataZoomModel.getPercentRange(),e=this._getViewExtent();this._handleEnds=[_M(t[0],[0,100],e,!0),_M(t[1],[0,100],e,!0)]},_updateInterval:function(t,e){var n=this.dataZoomModel,i=this._handleEnds,r=this._getViewExtent(),o=n.findRepresentativeAxisProxy().getMinMaxSpan(),a=[0,100];yM(e,i,r,n.get("zoomLock")?"all":t,null!=o.minSpan?_M(o.minSpan,a,r,!0):null,null!=o.maxSpan?_M(o.maxSpan,a,r,!0):null);var s=this._range,l=this._range=wM([_M(i[0],r,a,!0),_M(i[1],r,a,!0)]);return!s||s[0]!==l[0]||s[1]!==l[1]},_updateView:function(t){var e=this._displayables,n=this._handleEnds,i=wM(n.slice()),r=this._size;MM([0,1],function(t){var i=e.handles[t],o=this._handleHeight;i.attr({scale:[o/2,o/2],position:[n[t],r[1]/2-o/2]})},this),e.filler.setShape({x:i[0],y:0,width:i[1]-i[0],height:r[1]}),this._updateDataInfo(t)},_updateDataInfo:function(t){function e(t){var e=lr(i.handles[t].parent,this.group),n=hr(0===t?"right":"left",e),s=this._handleWidth/2+IM,l=ur([c[t]+(0===t?-s:s),this._size[1]/2],e);r[t].setStyle({x:l[0],y:l[1],textVerticalAlign:o===SM?"middle":n,textAlign:o===SM?n:"center",text:a[t]})}var n=this.dataZoomModel,i=this._displayables,r=i.handleLabels,o=this._orient,a=["",""];if(n.get("showDetail")){var s=n.findRepresentativeAxisProxy();if(s){var l=s.getAxisModel().axis,u=this._range,h=t?s.calculateDataWindow({start:u[0],end:u[1]}).valueWindow:s.getDataValueWindow();a=[this._formatLabel(h[0],l),this._formatLabel(h[1],l)]}}var c=wM(this._handleEnds.slice());e.call(this,0),e.call(this,1)},_formatLabel:function(t,e){var n=this.dataZoomModel,i=n.get("labelFormatter"),r=n.get("labelPrecision");null!=r&&"auto"!==r||(r=e.getPixelPrecision());var o=null==t||isNaN(t)?"":"category"===e.type||"time"===e.type?e.scale.getLabel(Math.round(t)):t.toFixed(Math.min(r,20));return x(i)?i(t,o):_(i)?i.replace("{value}",o):o},_showDataInfo:function(t){t=this._dragging||t;var e=this._displayables.handleLabels;e[0].attr("invisible",!t),e[1].attr("invisible",!t)},_onDragMove:function(t,e,n){this._dragging=!0;var i=ur([e,n],this._displayables.barGroup.getLocalTransform(),!0),r=this._updateInterval(t,i[0]),o=this.dataZoomModel.get("realtime");this._updateView(!o),r&&o&&this._dispatchZoomAction()},_onDragEnd:function(){this._dragging=!1,this._showDataInfo(!1),!this.dataZoomModel.get("realtime")&&this._dispatchZoomAction()},_onClickPanelClick:function(t){var e=this._size,n=this._displayables.barGroup.transformCoordToLocal(t.offsetX,t.offsetY);if(!(n[0]<0||n[0]>e[0]||n[1]<0||n[1]>e[1])){var i=this._handleEnds,r=(i[0]+i[1])/2,o=this._updateInterval("all",n[0]-r);this._updateView(),o&&this._dispatchZoomAction()}},_dispatchZoomAction:function(){var t=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,start:t[0],end:t[1]})},_findCoordRect:function(){var t;if(MM(this.getTargetCoordInfo(),function(e){if(!t&&e.length){var n=e[0].model.coordinateSystem;t=n.getRect&&n.getRect()}}),!t){var e=this.api.getWidth(),n=this.api.getHeight();t={x:.2*e,y:.2*n,width:.6*e,height:.6*n}}return t}});mM.extend({type:"dataZoom.inside",defaultOption:{disabled:!1,zoomLock:!1,zoomOnMouseWheel:!0,moveOnMouseMove:!0,preventDefaultMouseMove:!0}});var DM="\0_ec_interaction_mutex";ts({type:"takeGlobalCursor",event:"globalCursorTaken",update:"update"},function(){}),h(ed,Zp);var AM=v,kM="\0_ec_dataZoom_roams",PM=m,LM=vM.extend({type:"dataZoom.inside",init:function(t,e){this._range},render:function(t,e,n,i){LM.superApply(this,"render",arguments),this._range=t.getPercentRange(),d(this.getTargetCoordInfo(),function(e,i){var r=f(e,function(t){return cd(t.model)});d(e,function(e){var o=e.model,a=t.option;ud(n,{coordId:cd(o),allCoordIds:r,containsPoint:function(t,e,n){return o.coordinateSystem.containPoint([e,n])},dataZoomId:t.id,throttleRate:t.get("throttle",!0),panGetRange:PM(this._onPan,this,e,i),zoomGetRange:PM(this._onZoom,this,e,i),zoomLock:a.zoomLock,disabled:a.disabled,roamControllerOpt:{zoomOnMouseWheel:a.zoomOnMouseWheel,moveOnMouseMove:a.moveOnMouseMove,preventDefaultMouseMove:a.preventDefaultMouseMove}})},this)},this)},dispose:function(){hd(this.api,this.dataZoomModel.id),LM.superApply(this,"dispose",arguments),this._range=null},_onPan:function(t,e,n,i,r,o,a,s,l){var u=this._range,h=u.slice(),c=t.axisModels[0];if(c){var d=OM[e]([o,a],[s,l],c,n,t),f=d.signal*(h[1]-h[0])*d.pixel/d.pixelLength;return yM(f,h,[0,100],"all"),this._range=h,u[0]!==h[0]||u[1]!==h[1]?h:void 0}},_onZoom:function(t,e,n,i,r,o){var a=this._range,s=a.slice(),l=t.axisModels[0];if(l){var u=OM[e](null,[r,o],l,n,t),h=(u.signal>0?u.pixelStart+u.pixelLength-u.pixel:u.pixel-u.pixelStart)/u.pixelLength*(s[1]-s[0])+s[0];i=Math.max(1/i,0),s[0]=(s[0]-h)*i+h,s[1]=(s[1]-h)*i+h;var c=this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();return yM(0,s,[0,100],0,c.minSpan,c.maxSpan),this._range=s,a[0]!==s[0]||a[1]!==s[1]?s:void 0}}}),OM={grid:function(t,e,n,i,r){var o=n.axis,a={},s=r.model.coordinateSystem.getRect();return t=t||[0,0],"x"===o.dim?(a.pixel=e[0]-t[0],a.pixelLength=s.width,a.pixelStart=s.x,a.signal=o.inverse?1:-1):(a.pixel=e[1]-t[1],a.pixelLength=s.height,a.pixelStart=s.y,a.signal=o.inverse?-1:1),a},polar:function(t,e,n,i,r){var o=n.axis,a={},s=r.model.coordinateSystem,l=s.getRadiusAxis().getExtent(),u=s.getAngleAxis().getExtent();return t=t?s.pointToCoord(t):[0,0],e=s.pointToCoord(e),"radiusAxis"===n.mainType?(a.pixel=e[0]-t[0],a.pixelLength=l[1]-l[0],a.pixelStart=l[0],a.signal=o.inverse?1:-1):(a.pixel=e[1]-t[1],a.pixelLength=u[1]-u[0],a.pixelStart=u[0],a.signal=o.inverse?-1:1),a},singleAxis:function(t,e,n,i,r){var o=n.axis,a=r.model.coordinateSystem.getRect(),s={};return t=t||[0,0],"horizontal"===o.orient?(s.pixel=e[0]-t[0],s.pixelLength=a.width,s.pixelStart=a.x,s.signal=o.inverse?1:-1):(s.pixel=e[1]-t[1],s.pixelLength=a.height,s.pixelStart=a.y,s.signal=o.inverse?-1:1),s}};Ja({getTargetSeries:function(t){var e=N();return t.eachComponent("dataZoom",function(t){t.eachTargetAxis(function(t,n,i){d(i.getAxisProxy(t.name,n).getTargetSeriesModels(),function(t){e.set(t.uid,t)})})}),e},modifyOutputEnd:!0,overallReset:function(t,e){t.eachComponent("dataZoom",function(t){t.eachTargetAxis(function(t,n,i){i.getAxisProxy(t.name,n).reset(i,e)}),t.eachTargetAxis(function(t,n,i){i.getAxisProxy(t.name,n).filterData(i,e)})}),t.eachComponent("dataZoom",function(t){var e=t.findRepresentativeAxisProxy(),n=e.getDataPercentWindow(),i=e.getDataValueWindow();t.setRawRange({start:n[0],end:n[1],startValue:i[0],endValue:i[1]},!0)})}}),ts("dataZoom",function(t,e){var n=Fc(m(e.eachComponent,e,"dataZoom"),hM,function(t,e){return t.get(e.axisIndex)}),i=[];e.eachComponent({mainType:"dataZoom",query:t},function(t,e){i.push.apply(i,n(t).nodes)}),d(i,function(e,n){e.setRawRange({start:t.start,end:t.end,startValue:t.startValue,endValue:t.endValue})})});var zM={},EM=os({type:"toolbox",layoutMode:{type:"box",ignoreSize:!0},optionUpdated:function(){EM.superApply(this,"optionUpdated",arguments),d(this.option.feature,function(t,e){var n=wd(e);n&&i(t,n.defaultOption)})},defaultOption:{show:!0,z:6,zlevel:0,orient:"horizontal",left:"right",top:"top",backgroundColor:"transparent",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemSize:15,itemGap:8,showTitle:!0,iconStyle:{borderColor:"#666",color:"none"},emphasis:{iconStyle:{borderColor:"#3E98C5"}}}});as({type:"toolbox",render:function(t,e,n,i){function r(r,a){var s,c=h[r],d=h[a],f=new pr(l[c],t,t.ecModel);if(c&&!d){if(bd(c))s={model:f,onclick:f.option.onclick,featureName:c};else{var p=wd(c);if(!p)return;s=new p(f,e,n)}u[c]=s}else{if(!(s=u[d]))return;s.model=f,s.ecModel=e,s.api=n}c||!d?f.get("show")&&!s.unusable?(o(f,s,c),f.setIconStatus=function(t,e){var n=this.option,i=this.iconPaths;n.iconStatus=n.iconStatus||{},n.iconStatus[t]=e,i[t]&&i[t].trigger(e)},s.render&&s.render(f,e,n,i)):s.remove&&s.remove(e,n):s.dispose&&s.dispose(e,n)}function o(i,r,o){var l=i.getModel("iconStyle"),u=i.getModel("emphasis.iconStyle"),h=r.getIcons?r.getIcons():i.get("icon"),c=i.get("title")||{};if("string"==typeof h){var f=h,p=c;c={},(h={})[o]=f,c[o]=p}var g=i.iconPaths={};d(h,function(o,h){var d=fr(o,{},{x:-s/2,y:-s/2,width:s,height:s});d.setStyle(l.getItemStyle()),d.hoverStyle=u.getItemStyle(),qi(d),t.get("showTitle")&&(d.__title=c[h],d.on("mouseover",function(){var t=u.getItemStyle();d.setStyle({text:c[h],textPosition:t.textPosition||"bottom",textFill:t.fill||t.stroke||"#000",textAlign:t.textAlign||"center"})}).on("mouseout",function(){d.setStyle({textFill:null})})),d.trigger(i.get("iconStatus."+h)||"normal"),a.add(d),d.on("click",m(r.onclick,r,e,n,h)),g[h]=d})}var a=this.group;if(a.removeAll(),t.get("show")){var s=+t.get("itemSize"),l=t.get("feature")||{},u=this._features||(this._features={}),h=[];d(l,function(t,e){h.push(e)}),new hs(this._featureNames||[],h).add(r).update(r).remove(v(r,null)).execute(),this._featureNames=h,Jh(a,t,n),a.add(tc(a.getBoundingRect(),t)),a.eachChild(function(t){var e=t.__title,i=t.hoverStyle;if(i&&e){var r=ce(e,Ce(i)),o=t.position[0]+a.position[0],l=!1;t.position[1]+a.position[1]+s+r.height>n.getHeight()&&(i.textPosition="top",l=!0);var u=l?-5-r.height:s+8;o+r.width/2>n.getWidth()?(i.textPosition=["100%",u],i.textAlign="right"):o-r.width/2<0&&(i.textPosition=[0,u],i.textAlign="left")}})}},updateView:function(t,e,n,i){d(this._features,function(t){t.updateView&&t.updateView(t.model,e,n,i)})},remove:function(t,e){d(this._features,function(n){n.remove&&n.remove(t,e)}),this.group.removeAll()},dispose:function(t,e){d(this._features,function(n){n.dispose&&n.dispose(t,e)})}});var NM=Sx.toolbox.saveAsImage;Md.defaultOption={show:!0,icon:"M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0",title:NM.title,type:"png",name:"",excludeComponents:["toolbox"],pixelRatio:1,lang:NM.lang.slice()},Md.prototype.unusable=!bp.canvasSupported,Md.prototype.onclick=function(t,e){var n=this.model,i=n.get("name")||t.get("title.0.text")||"echarts",r=document.createElement("a"),o=n.get("type",!0)||"png";r.download=i+"."+o,r.target="_blank";var a=e.getConnectedDataURL({type:o,backgroundColor:n.get("backgroundColor",!0)||t.get("backgroundColor")||"#fff",excludeComponents:n.get("excludeComponents"),pixelRatio:n.get("pixelRatio")});if(r.href=a,"function"!=typeof MouseEvent||bp.browser.ie||bp.browser.edge)if(window.navigator.msSaveOrOpenBlob){for(var s=atob(a.split(",")[1]),l=s.length,u=new Uint8Array(l);l--;)u[l]=s.charCodeAt(l);var h=new Blob([u]);window.navigator.msSaveOrOpenBlob(h,i+"."+o)}else{var c=n.get("lang"),d='';window.open().document.write(d)}else{var f=new MouseEvent("click",{view:window,bubbles:!0,cancelable:!1});r.dispatchEvent(f)}},_d("saveAsImage",Md);var RM=Sx.toolbox.magicType;Sd.defaultOption={show:!0,type:[],icon:{line:"M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4",bar:"M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7",stack:"M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z",tiled:"M2.3,2.2h22.8V25H2.3V2.2z M35,2.2h22.8V25H35V2.2zM2.3,35h22.8v22.8H2.3V35z M35,35h22.8v22.8H35V35z"},title:n(RM.title),option:{},seriesIndex:{}};var BM=Sd.prototype;BM.getIcons=function(){var t=this.model,e=t.get("icon"),n={};return d(t.get("type"),function(t){e[t]&&(n[t]=e[t])}),n};var VM={line:function(t,e,n,r){if("bar"===t)return i({id:e,type:"line",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},r.get("option.line")||{},!0)},bar:function(t,e,n,r){if("line"===t)return i({id:e,type:"bar",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},r.get("option.bar")||{},!0)},stack:function(t,e,n,r){if("line"===t||"bar"===t)return i({id:e,stack:"__ec_magicType_stack__"},r.get("option.stack")||{},!0)},tiled:function(t,e,n,r){if("line"===t||"bar"===t)return i({id:e,stack:""},r.get("option.tiled")||{},!0)}},FM=[["line","bar"],["stack","tiled"]];BM.onclick=function(t,e,n){var i=this.model,r=i.get("seriesIndex."+n);if(VM[n]){var o={series:[]};d(FM,function(t){l(t,n)>=0&&d(t,function(t){i.setIconStatus(t,"normal")})}),i.setIconStatus(n,"emphasis"),t.eachComponent({mainType:"series",query:null==r?null:{seriesIndex:r}},function(e){var r=e.subType,s=e.id,l=VM[n](r,s,e,i);l&&(a(l,e.option),o.series.push(l));var u=e.coordinateSystem;if(u&&"cartesian2d"===u.type&&("line"===n||"bar"===n)){var h=u.getAxesByScale("ordinal")[0];if(h){var c=h.dim+"Axis",d=t.queryComponents({mainType:c,index:e.get(name+"Index"),id:e.get(name+"Id")})[0].componentIndex;o[c]=o[c]||[];for(var f=0;f<=d;f++)o[c][d]=o[c][d]||{};o[c][d].boundaryGap="bar"===n}}}),e.dispatchAction({type:"changeMagicType",currentType:n,newOption:o})}},ts({type:"changeMagicType",event:"magicTypeChanged",update:"prepareAndUpdate"},function(t,e){e.mergeOption(t.newOption)}),_d("magicType",Sd);var HM=Sx.toolbox.dataView,GM=new Array(60).join("-"),WM="\t",ZM=new RegExp("["+WM+"]+","g");zd.defaultOption={show:!0,readOnly:!1,optionToContent:null,contentToOption:null,icon:"M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28",title:n(HM.title),lang:n(HM.lang),backgroundColor:"#fff",textColor:"#000",textareaColor:"#fff",textareaBorderColor:"#333",buttonColor:"#c23531",buttonTextColor:"#fff"},zd.prototype.onclick=function(t,e){function n(){i.removeChild(o),x._dom=null}var i=e.getDom(),r=this.model;this._dom&&i.removeChild(this._dom);var o=document.createElement("div");o.style.cssText="position:absolute;left:5px;top:5px;bottom:5px;right:5px;",o.style.backgroundColor=r.get("backgroundColor")||"#fff";var a=document.createElement("h4"),s=r.get("lang")||[];a.innerHTML=s[0]||r.get("title"),a.style.cssText="margin: 10px 20px;",a.style.color=r.get("textColor");var l=document.createElement("div"),u=document.createElement("textarea");l.style.cssText="display:block;width:100%;overflow:auto;";var h=r.get("optionToContent"),c=r.get("contentToOption"),d=Dd(t);if("function"==typeof h){var f=h(e.getOption());"string"==typeof f?l.innerHTML=f:S(f)&&l.appendChild(f)}else l.appendChild(u),u.readOnly=r.get("readOnly"),u.style.cssText="width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;",u.style.color=r.get("textColor"),u.style.borderColor=r.get("textareaBorderColor"),u.style.backgroundColor=r.get("textareaColor"),u.value=d.value;var p=d.meta,g=document.createElement("div");g.style.cssText="position:absolute;bottom:0;left:0;right:0;";var m="float:right;margin-right:20px;border:none;cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px",v=document.createElement("div"),y=document.createElement("div");m+=";background-color:"+r.get("buttonColor"),m+=";color:"+r.get("buttonTextColor");var x=this;on(v,"click",n),on(y,"click",function(){var t;try{t="function"==typeof c?c(l,e.getOption()):Od(u.value,p)}catch(t){throw n(),new Error("Data view format error "+t)}t&&e.dispatchAction({type:"changeDataView",newOption:t}),n()}),v.innerHTML=s[1],y.innerHTML=s[2],y.style.cssText=m,v.style.cssText=m,!r.get("readOnly")&&g.appendChild(y),g.appendChild(v),on(u,"keydown",function(t){if(9===(t.keyCode||t.which)){var e=this.value,n=this.selectionStart,i=this.selectionEnd;this.value=e.substring(0,n)+WM+e.substring(i),this.selectionStart=this.selectionEnd=n+1,tm(t)}}),o.appendChild(a),o.appendChild(l),o.appendChild(g),l.style.height=i.clientHeight-80+"px",i.appendChild(o),this._dom=o},zd.prototype.remove=function(t,e){this._dom&&e.getDom().removeChild(this._dom)},zd.prototype.dispose=function(t,e){this.remove(t,e)},_d("dataView",zd),ts({type:"changeDataView",event:"dataViewChanged",update:"prepareAndUpdate"},function(t,e){var n=[];d(t.newOption.series,function(t){var i=e.getSeriesByName(t.name)[0];if(i){var r=i.get("data");n.push({name:t.name,data:Ed(t.data,r)})}else n.push(o({type:"scatter"},t))}),e.mergeOption(a({series:n},t.newOption))});var UM=v,XM=d,jM=f,YM=Math.min,qM=Math.max,$M=Math.pow,KM=1e4,QM=6,JM=6,tS="globalPan",eS={w:[0,0],e:[0,1],n:[1,0],s:[1,1]},nS={w:"ew",e:"ew",n:"ns",s:"ns",ne:"nesw",sw:"nesw",nw:"nwse",se:"nwse"},iS={brushStyle:{lineWidth:2,stroke:"rgba(0,0,0,0.3)",fill:"rgba(0,0,0,0.1)"},transformable:!0,brushMode:"single",removeOnClick:!1},rS=0;Nd.prototype={constructor:Nd,enableBrush:function(t){return this._brushType&&Bd(this),t.brushType&&Rd(this,t),this},setPanels:function(t){if(t&&t.length){var e=this._panels={};d(t,function(t){e[t.panelId]=n(t)})}else this._panels=null;return this},mount:function(t){t=t||{},this._enableGlobalPan=t.enableGlobalPan;var e=this.group;return this._zr.add(e),e.attr({position:t.position||[0,0],rotation:t.rotation||0,scale:t.scale||[1,1]}),this._transform=e.getLocalTransform(),this},eachCover:function(t,e){XM(this._covers,t,e)},updateCovers:function(t){function e(t,e){return(null!=t.id?t.id:o+e)+"-"+t.brushType}function r(e,n){var i=t[e];if(null!=n&&a[n]===u)s[e]=a[n];else{var r=s[e]=null!=n?(a[n].__brushOption=i,a[n]):Fd(l,Vd(l,i));Wd(l,r)}}t=f(t,function(t){return i(n(iS),t,!0)});var o="\0-brush-index-",a=this._covers,s=this._covers=[],l=this,u=this._creatingCover;return new hs(a,t,function(t,n){return e(t.__brushOption,n)},e).add(r).update(r).remove(function(t){a[t]!==u&&l.group.remove(a[t])}).execute(),this},unmount:function(){return this.enableBrush(!1),jd(this),this._zr.remove(this.group),this},dispose:function(){this.unmount(),this.off()}},h(Nd,Zp);var oS={mousedown:function(t){if(this._dragging)mf.call(this,t);else if(!t.target||!t.target.draggable){df(t);var e=this.group.transformCoordToLocal(t.offsetX,t.offsetY);this._creatingCover=null,(this._creatingPanel=Ud(this,t,e))&&(this._dragging=!0,this._track=[e.slice()])}},mousemove:function(t){var e=this.group.transformCoordToLocal(t.offsetX,t.offsetY);if(cf(this,t,e),this._dragging){df(t);var n=pf(this,t,e,!1);n&&Yd(this,n)}},mouseup:mf},aS={lineX:vf(0),lineY:vf(1),rect:{createCover:function(t,e){return Kd(UM(af,function(t){return t},function(t){return t}),t,e,["w","e","n","s","se","sw","ne","nw"])},getCreatingRange:function(t){var e=$d(t);return nf(e[1][0],e[1][1],e[0][0],e[0][1])},updateCoverShape:function(t,e,n,i){Qd(t,e,n,i)},updateCommon:Jd,contain:ff},polygon:{createCover:function(t,e){var n=new Sg;return n.add(new Vv({name:"main",style:ef(e),silent:!0})),n},getCreatingRange:function(t){return t},endCreating:function(t,e){e.remove(e.childAt(0)),e.add(new Bv({name:"main",draggable:!0,drift:UM(sf,t,e),ondragend:UM(Yd,t,{isEnd:!0})}))},updateCoverShape:function(t,e,n,i){e.childAt(0).setShape({points:uf(t,e,n)})},updateCommon:Jd,contain:ff}},sS={axisPointer:1,tooltip:1,brush:1},lS=d,uS=l,hS=v,cS=["dataToPoint","pointToData"],dS=["grid","xAxis","yAxis","geo","graph","polar","radiusAxis","angleAxis","bmap"],fS=Mf.prototype;fS.setOutputRanges=function(t,e){this.matchOutputRanges(t,e,function(t,e,n){if((t.coordRanges||(t.coordRanges=[])).push(e),!t.coordRange){t.coordRange=e;var i=vS[t.brushType](0,n,e);t.__rangeOffset={offset:yS[t.brushType](i.values,t.range,[1,1]),xyMinMax:i.xyMinMax}}})},fS.matchOutputRanges=function(t,e,n){lS(t,function(t){var i=this.findTargetInfo(t,e);i&&!0!==i&&d(i.coordSyses,function(i){var r=vS[t.brushType](1,i,t.range);n(t,r.values,i,e)})},this)},fS.setInputRanges=function(t,e){lS(t,function(t){var n=this.findTargetInfo(t,e);if(t.range=t.range||[],n&&!0!==n){t.panelId=n.panelId;var i=vS[t.brushType](0,n.coordSys,t.coordRange),r=t.__rangeOffset;t.range=r?yS[t.brushType](i.values,r.offset,Df(i.xyMinMax,r.xyMinMax)):i.values}},this)},fS.makePanelOpts=function(t,e){return f(this._targetInfoList,function(n){var i=n.getPanelRect();return{panelId:n.panelId,defaultBrushType:e&&e(n),clipPath:xf(i),isTargetByCursor:wf(i,t,n.coordSysModel),getLinearBrushOtherExtent:_f(i)}})},fS.controlSeries=function(t,e,n){var i=this.findTargetInfo(t,n);return!0===i||i&&uS(i.coordSyses,e.coordinateSystem)>=0},fS.findTargetInfo=function(t,e){for(var n=this._targetInfoList,i=If(e,t),r=0;r=0||uS(i,t.getAxis("y").model)>=0)&&o.push(t)}),e.push({panelId:"grid--"+t.id,gridModel:t,coordSysModel:t,coordSys:o[0],coordSyses:o,getPanelRect:mS.grid,xAxisDeclared:a[t.id],yAxisDeclared:s[t.id]})}))},geo:function(t,e){lS(t.geoModels,function(t){var n=t.coordinateSystem;e.push({panelId:"geo--"+t.id,geoModel:t,coordSysModel:t,coordSys:n,coordSyses:[n],getPanelRect:mS.geo})})}},gS=[function(t,e){var n=t.xAxisModel,i=t.yAxisModel,r=t.gridModel;return!r&&n&&(r=n.axis.grid.model),!r&&i&&(r=i.axis.grid.model),r&&r===e.gridModel},function(t,e){var n=t.geoModel;return n&&n===e.geoModel}],mS={grid:function(){return this.coordSys.grid.getRect().clone()},geo:function(){var t=this.coordSys,e=t.getBoundingRect().clone();return e.applyTransform(lr(t)),e}},vS={lineX:hS(Cf,0),lineY:hS(Cf,1),rect:function(t,e,n){var i=e[cS[t]]([n[0][0],n[1][0]]),r=e[cS[t]]([n[0][1],n[1][1]]),o=[Sf([i[0],r[0]]),Sf([i[1],r[1]])];return{values:o,xyMinMax:o}},polygon:function(t,e,n){var i=[[1/0,-1/0],[1/0,-1/0]];return{values:f(n,function(n){var r=e[cS[t]](n);return i[0][0]=Math.min(i[0][0],r[0]),i[1][0]=Math.min(i[1][0],r[1]),i[0][1]=Math.max(i[0][1],r[0]),i[1][1]=Math.max(i[1][1],r[1]),r}),xyMinMax:i}}},yS={lineX:hS(Tf,0),lineY:hS(Tf,1),rect:function(t,e,n){return[[t[0][0]-n[0]*e[0][0],t[0][1]-n[0]*e[0][1]],[t[1][0]-n[1]*e[1][0],t[1][1]-n[1]*e[1][1]]]},polygon:function(t,e,n){return f(t,function(t,i){return[t[0]-n[0]*e[i][0],t[1]-n[1]*e[i][1]]})}},xS=d,_S="\0_ec_hist_store";mM.extend({type:"dataZoom.select"}),vM.extend({type:"dataZoom.select"});var wS=Sx.toolbox.dataZoom,bS=d,MS="\0_ec_\0toolbox-dataZoom_";Ef.defaultOption={show:!0,icon:{zoom:"M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1",back:"M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26"},title:n(wS.title)};var SS=Ef.prototype;SS.render=function(t,e,n,i){this.model=t,this.ecModel=e,this.api=n,Bf(t,e,this,i,n),Rf(t,e)},SS.onclick=function(t,e,n){IS[n].call(this)},SS.remove=function(t,e){this._brushController.unmount()},SS.dispose=function(t,e){this._brushController.dispose()};var IS={zoom:function(){var t=!this._isZoomActive;this.api.dispatchAction({type:"takeGlobalCursor",key:"dataZoomSelect",dataZoomSelectActive:t})},back:function(){this._dispatchZoomAction(Pf(this.ecModel))}};SS._onBrush=function(t,e){function n(t,e,n){var a=e.getAxis(t),s=a.model,l=i(t,s,o),u=l.findRepresentativeAxisProxy(s).getMinMaxSpan();null==u.minValueSpan&&null==u.maxValueSpan||(n=yM(0,n.slice(),a.scale.getExtent(),0,u.minValueSpan,u.maxValueSpan)),l&&(r[l.id]={dataZoomId:l.id,startValue:n[0],endValue:n[1]})}function i(t,e,n){var i;return n.eachComponent({mainType:"dataZoom",subType:"select"},function(n){n.getAxisModel(t,e.componentIndex)&&(i=n)}),i}if(e.isEnd&&t.length){var r={},o=this.ecModel;this._brushController.updateCovers([]),new Mf(Nf(this.model.option),o,{include:["grid"]}).matchOutputRanges(t,o,function(t,e,i){if("cartesian2d"===i.type){var r=t.brushType;"rect"===r?(n("x",i,e[0]),n("y",i,e[1])):n({lineX:"x",lineY:"y"}[r],i,e)}}),kf(o,r),this._dispatchZoomAction(r)}},SS._dispatchZoomAction=function(t){var e=[];bS(t,function(t,i){e.push(n(t))}),e.length&&this.api.dispatchAction({type:"dataZoom",from:this.uid,batch:e})},_d("dataZoom",Ef),Qa(function(t){function e(t,e){if(e){var r=t+"Index",o=e[r];null==o||"all"==o||y(o)||(o=!1===o||"none"===o?[]:[o]),n(t,function(e,n){if(null==o||"all"==o||-1!==l(o,n)){var a={type:"select",$fromToolbox:!0,id:MS+t+n};a[r]=n,i.push(a)}})}}function n(e,n){var i=t[e];y(i)||(i=i?[i]:[]),bS(i,n)}if(t){var i=t.dataZoom||(t.dataZoom=[]);y(i)||(t.dataZoom=i=[i]);var r=t.toolbox;if(r&&(y(r)&&(r=r[0]),r&&r.feature)){var o=r.feature.dataZoom;e("xAxis",o),e("yAxis",o)}}});var CS=Sx.toolbox.restore;Vf.defaultOption={show:!0,icon:"M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5",title:CS.title},Vf.prototype.onclick=function(t,e,n){Lf(t),e.dispatchAction({type:"restore",from:this.uid})},_d("restore",Vf),ts({type:"restore",event:"restore",update:"prepareAndUpdate"},function(t,e){e.resetOption("recreate")});var TS,DS="urn:schemas-microsoft-com:vml",AS="undefined"==typeof window?null:window,kS=!1,PS=AS&&AS.document;if(PS&&!bp.canvasSupported)try{!PS.namespaces.zrvml&&PS.namespaces.add("zrvml",DS),TS=function(t){return PS.createElement("')}}catch(t){TS=function(t){return PS.createElement("<"+t+' xmlns="'+DS+'" class="zrvml">')}}var LS=av.CMD,OS=Math.round,zS=Math.sqrt,ES=Math.abs,NS=Math.cos,RS=Math.sin,BS=Math.max;if(!bp.canvasSupported){var VS=21600,FS=VS/2,HS=function(t){t.style.cssText="position:absolute;left:0;top:0;width:1px;height:1px;",t.coordsize=VS+","+VS,t.coordorigin="0,0"},GS=function(t){return String(t).replace(/&/g,"&").replace(/"/g,""")},WS=function(t,e,n){return"rgb("+[t,e,n].join(",")+")"},ZS=function(t,e){e&&t&&e.parentNode!==t&&t.appendChild(e)},US=function(t,e){e&&t&&e.parentNode===t&&t.removeChild(e)},XS=function(t,e,n){return 1e5*(parseFloat(t)||0)+1e3*(parseFloat(e)||0)+n},jS=function(t,e){return"string"==typeof t?t.lastIndexOf("%")>=0?parseFloat(t)/100*e:parseFloat(t):t},YS=function(t,e,n){var i=St(e);n=+n,isNaN(n)&&(n=1),i&&(t.color=WS(i[0],i[1],i[2]),t.opacity=n*i[3])},qS=function(t){var e=St(t);return[WS(e[0],e[1],e[2]),e[3]]},$S=function(t,e,n){var i=e.fill;if(null!=i)if(i instanceof Xv){var r,o=0,a=[0,0],s=0,l=1,u=n.getBoundingRect(),h=u.width,c=u.height;if("linear"===i.type){r="gradient";var d=n.transform,f=[i.x*h,i.y*c],p=[i.x2*h,i.y2*c];d&&($(f,f,d),$(p,p,d));var g=p[0]-f[0],m=p[1]-f[1];(o=180*Math.atan2(g,m)/Math.PI)<0&&(o+=360),o<1e-6&&(o=0)}else{r="gradientradial";var f=[i.x*h,i.y*c],d=n.transform,v=n.scale,y=h,x=c;a=[(f[0]-u.x)/y,(f[1]-u.y)/x],d&&$(f,f,d),y/=v[0]*VS,x/=v[1]*VS;var _=BS(y,x);s=0/_,l=2*i.r/_-s}var w=i.colorStops.slice();w.sort(function(t,e){return t.offset-e.offset});for(var b=w.length,M=[],S=[],I=0;I=2){var D=M[0][0],A=M[1][0],k=M[0][1]*e.opacity,P=M[1][1]*e.opacity;t.type=r,t.method="none",t.focus="100%",t.angle=o,t.color=D,t.color2=A,t.colors=S.join(","),t.opacity=P,t.opacity2=k}"radial"===r&&(t.focusposition=a.join(","))}else YS(t,i,e.opacity)},KS=function(t,e){null!=e.lineDash&&(t.dashstyle=e.lineDash.join(" ")),null==e.stroke||e.stroke instanceof Xv||YS(t,e.stroke,e.opacity)},QS=function(t,e,n,i){var r="fill"==e,o=t.getElementsByTagName(e)[0];null!=n[e]&&"none"!==n[e]&&(r||!r&&n.lineWidth)?(t[r?"filled":"stroked"]="true",n[e]instanceof Xv&&US(t,o),o||(o=Ff(e)),r?$S(o,n,i):KS(o,n),ZS(t,o)):(t[r?"filled":"stroked"]="false",US(t,o))},JS=[[],[],[]],tI=function(t,e){var n,i,r,o,a,s,l=LS.M,u=LS.C,h=LS.L,c=LS.A,d=LS.Q,f=[],p=t.data,g=t.len();for(o=0;o.01?O&&(z+=.0125):Math.abs(E-D)<1e-4?O&&zT?x-=.0125:x+=.0125:O&&ED?y+=.0125:y-=.0125),f.push(N,OS(((T-A)*S+b)*VS-FS),",",OS(((D-k)*I+M)*VS-FS),",",OS(((T+A)*S+b)*VS-FS),",",OS(((D+k)*I+M)*VS-FS),",",OS((z*S+b)*VS-FS),",",OS((E*I+M)*VS-FS),",",OS((y*S+b)*VS-FS),",",OS((x*I+M)*VS-FS)),a=y,s=x;break;case LS.R:var R=JS[0],B=JS[1];R[0]=p[o++],R[1]=p[o++],B[0]=R[0]+p[o++],B[1]=R[1]+p[o++],e&&($(R,R,e),$(B,B,e)),R[0]=OS(R[0]*VS-FS),B[0]=OS(B[0]*VS-FS),R[1]=OS(R[1]*VS-FS),B[1]=OS(B[1]*VS-FS),f.push(" m ",R[0],",",R[1]," l ",B[0],",",R[1]," l ",B[0],",",B[1]," l ",R[0],",",B[1]);break;case LS.Z:f.push(" x ")}if(n>0){f.push(i);for(var V=0;V100&&(rI=0,iI={});var n,i=oI.style;try{i.font=t,n=i.fontFamily.split(",")[0]}catch(t){}e={style:i.fontStyle||"normal",variant:i.fontVariant||"normal",weight:i.fontWeight||"normal",size:0|parseFloat(i.fontSize||12),family:n||"Microsoft YaHei"},iI[t]=e,rI++}return e};!function(t,e){Zg[t]=e}("measureText",function(t,e){var n=PS;nI||((nI=n.createElement("div")).style.cssText="position:absolute;top:-20000px;left:0;padding:0;margin:0;border:none;white-space:pre;",PS.body.appendChild(nI));try{nI.style.font=e}catch(t){}return nI.innerHTML="",nI.appendChild(n.createTextNode(t)),{width:nI.offsetWidth}});for(var sI=new Xt,lI=[Yg,Xe,je,xi,kv],uI=0;uI=r&&u+1>=o){for(var h=[],c=0;c=r&&c+1>=o)return ip(0,s.components);l[n]=s}else l[n]=void 0}a++}();if(d)return d}},pushComponent:function(t,e,n){var i=t[t.length-1];i&&i.added===e&&i.removed===n?t[t.length-1]={count:i.count+1,added:e,removed:n}:t.push({count:1,added:e,removed:n})},extractCommon:function(t,e,n,i){for(var r=e.length,o=n.length,a=t.newPos,s=a-i,l=0;a+1=0;--i)if(e[i]===t)return!0;return!1}),n):null:n[0]},op.prototype.update=function(t,e){if(t){var n=this.getDefs(!1);if(t[this._domName]&&n.contains(t[this._domName]))"function"==typeof e&&e(t);else{var i=this.add(t);i&&(t[this._domName]=i)}}},op.prototype.addDom=function(t){this.getDefs(!0).appendChild(t)},op.prototype.removeDom=function(t){var e=this.getDefs(!1);e&&t[this._domName]&&(e.removeChild(t[this._domName]),t[this._domName]=null)},op.prototype.getDoms=function(){var t=this.getDefs(!1);if(!t)return[];var e=[];return d(this._tagNames,function(n){var i=t.getElementsByTagName(n);e=e.concat([].slice.call(i))}),e},op.prototype.markAllUnused=function(){var t=this;d(this.getDoms(),function(e){e[t._markLabel]="0"})},op.prototype.markUsed=function(t){t&&(t[this._markLabel]="1")},op.prototype.removeUnused=function(){var t=this.getDefs(!1);if(t){var e=this;d(this.getDoms(),function(n){"1"!==n[e._markLabel]&&t.removeChild(n)})}},op.prototype.getSvgProxy=function(t){return t instanceof xi?bI:t instanceof je?MI:t instanceof kv?SI:bI},op.prototype.getTextSvgElement=function(t){return t.__textSvgEl},op.prototype.getSvgElement=function(t){return t.__svgEl},u(ap,op),ap.prototype.addWithoutUpdate=function(t,e){if(e&&e.style){var n=this;d(["fill","stroke"],function(i){if(e.style[i]&&("linear"===e.style[i].type||"radial"===e.style[i].type)){var r,o=e.style[i],a=n.getDefs(!0);o._dom?(r=o._dom,a.contains(o._dom)||n.addDom(r)):r=n.add(o),n.markUsed(e);var s=r.getAttribute("id");t.setAttribute(i,"url(#"+s+")")}})}},ap.prototype.add=function(t){var e;if("linear"===t.type)e=this.createElement("linearGradient");else{if("radial"!==t.type)return yg("Illegal gradient type."),null;e=this.createElement("radialGradient")}return t.id=t.id||this.nextId++,e.setAttribute("id","zr"+this._zrId+"-gradient-"+t.id),this.updateDom(t,e),this.addDom(e),e},ap.prototype.update=function(t){var e=this;op.prototype.update.call(this,t,function(){var n=t.type,i=t._dom.tagName;"linear"===n&&"linearGradient"===i||"radial"===n&&"radialGradient"===i?e.updateDom(t,t._dom):(e.removeDom(t),e.add(t))})},ap.prototype.updateDom=function(t,e){if("linear"===t.type)e.setAttribute("x1",t.x),e.setAttribute("y1",t.y),e.setAttribute("x2",t.x2),e.setAttribute("y2",t.y2);else{if("radial"!==t.type)return void yg("Illegal gradient type.");e.setAttribute("cx",t.x),e.setAttribute("cy",t.y),e.setAttribute("r",t.r)}t.global?e.setAttribute("gradientUnits","userSpaceOnUse"):e.setAttribute("gradientUnits","objectBoundingBox"),e.innerHTML="";for(var n=t.colorStops,i=0,r=n.length;i0){var i,r,o=this.getDefs(!0),a=e[0],s=n?"_textDom":"_dom";a[s]?(r=a[s].getAttribute("id"),i=a[s],o.contains(i)||o.appendChild(i)):(r="zr"+this._zrId+"-clip-"+this.nextId,++this.nextId,(i=this.createElement("clipPath")).setAttribute("id",r),o.appendChild(i),a[s]=i);var l=this.getSvgProxy(a);if(a.transform&&a.parent.invTransform&&!n){var u=Array.prototype.slice.call(a.transform);st(a.transform,a.parent.invTransform,a.transform),l.brush(a),a.transform=u}else l.brush(a);var h=this.getSvgElement(a);i.innerHTML="",i.appendChild(h.cloneNode()),t.setAttribute("clip-path","url(#"+r+")"),e.length>1&&this.updateDom(i,e.slice(1),n)}else t&&t.setAttribute("clip-path","none")},sp.prototype.markUsed=function(t){var e=this;t.__clipPaths&&t.__clipPaths.length>0&&d(t.__clipPaths,function(t){t._dom&&op.prototype.markUsed.call(e,t._dom),t._textDom&&op.prototype.markUsed.call(e,t._textDom)})},u(lp,op),lp.prototype.addWithoutUpdate=function(t,e){if(e&&up(e.style)){var n,i=e.style;i._shadowDom?(n=i._shadowDom,this.getDefs(!0).contains(i._shadowDom)||this.addDom(n)):n=this.add(e),this.markUsed(e);var r=n.getAttribute("id");t.style.filter="url(#"+r+")"}},lp.prototype.add=function(t){var e=this.createElement("filter"),n=t.style;return n._shadowDomId=n._shadowDomId||this.nextId++,e.setAttribute("id","zr"+this._zrId+"-shadow-"+n._shadowDomId),this.updateDom(t,e),this.addDom(e),e},lp.prototype.update=function(t,e){var n=e.style;if(up(n)){var i=this;op.prototype.update.call(this,e,function(t){i.updateDom(e,t._shadowDom)})}else this.remove(t,n)},lp.prototype.remove=function(t,e){null!=e._shadowDomId&&(this.removeDom(e),t.style.filter="")},lp.prototype.updateDom=function(t,e){var n=e.getElementsByTagName("feDropShadow");n=0===n.length?this.createElement("feDropShadow"):n[0];var i,r,o,a,s=t.style,l=t.scale?t.scale[0]||1:1,u=t.scale?t.scale[1]||1:1;if(s.shadowBlur||s.shadowOffsetX||s.shadowOffsetY)i=s.shadowOffsetX||0,r=s.shadowOffsetY||0,o=s.shadowBlur,a=s.shadowColor;else{if(!s.textShadowBlur)return void this.removeDom(e,s);i=s.textShadowOffsetX||0,r=s.textShadowOffsetY||0,o=s.textShadowBlur,a=s.textShadowColor}n.setAttribute("dx",i/l),n.setAttribute("dy",r/u),n.setAttribute("flood-color",a);var h=o/2/l+" "+o/2/u;n.setAttribute("stdDeviation",h),e.setAttribute("x","-100%"),e.setAttribute("y","-100%"),e.setAttribute("width",Math.ceil(o/2*200)+"%"),e.setAttribute("height",Math.ceil(o/2*200)+"%"),e.appendChild(n),s._shadowDom=e},lp.prototype.markUsed=function(t){var e=t.style;e&&e._shadowDom&&op.prototype.markUsed.call(this,e._shadowDom)};var AI=function(t,e,n,i){this.root=t,this.storage=e,this._opts=n=o({},n||{});var r=Uf("svg");r.setAttribute("xmlns","http://www.w3.org/2000/svg"),r.setAttribute("version","1.1"),r.setAttribute("baseProfile","full"),r.style.cssText="user-select:none;position:absolute;left:0;top:0;",this.gradientManager=new ap(i,r),this.clipPathManager=new sp(i,r),this.shadowManager=new lp(i,r);var a=document.createElement("div");a.style.cssText="overflow:hidden;position:relative",this._svgRoot=r,this._viewport=a,t.appendChild(a),a.appendChild(r),this.resize(n.width,n.height),this._visibleList=[]};AI.prototype={constructor:AI,getType:function(){return"svg"},getViewportRoot:function(){return this._viewport},getViewportRootOffset:function(){var t=this.getViewportRoot();if(t)return{offsetLeft:t.offsetLeft||0,offsetTop:t.offsetTop||0}},refresh:function(){var t=this.storage.getDisplayList(!0);this._paintList(t)},setBackgroundColor:function(t){this._viewport.style.background=t},_paintList:function(t){this.gradientManager.markAllUnused(),this.clipPathManager.markAllUnused(),this.shadowManager.markAllUnused();var e,n=this._svgRoot,i=this._visibleList,r=t.length,o=[];for(e=0;e=0;--i)if(e[i]===t)return!0;return!1}),n):null:n[0]},resize:function(t,e){var n=this._viewport;n.style.display="none";var i=this._opts;if(null!=t&&(i.width=t),null!=e&&(i.height=e),t=this._getSize(0),e=this._getSize(1),n.style.display="",this._width!==t||this._height!==e){this._width=t,this._height=e;var r=n.style;r.width=t+"px",r.height=e+"px";var o=this._svgRoot;o.setAttribute("width",t),o.setAttribute("height",e)}},getWidth:function(){return this._width},getHeight:function(){return this._height},_getSize:function(t){var e=this._opts,n=["width","height"][t],i=["clientWidth","clientHeight"][t],r=["paddingLeft","paddingTop"][t],o=["paddingRight","paddingBottom"][t];if(null!=e[n]&&"auto"!==e[n])return parseFloat(e[n]);var a=this.root,s=document.defaultView.getComputedStyle(a);return(a[i]||hp(s[n])||hp(a.style[n]))-(hp(s[r])||0)-(hp(s[o])||0)|0},dispose:function(){this.root.innerHTML="",this._svgRoot=this._viewport=this.storage=null},clear:function(){this._viewport&&this.root.removeChild(this._viewport)},pathToDataUrl:function(){return this.refresh(),"data:image/svg+xml;charset=UTF-8,"+this._svgRoot.outerHTML}},d(["getLayer","insertLayer","eachLayer","eachBuiltinLayer","eachOtherLayer","getLayers","modLayer","delLayer","clearLayer","toDataURL","pathToImage"],function(t){AI.prototype[t]=yp(t)}),vn("svg",AI),t.version="4.1.0",t.dependencies=Gx,t.PRIORITY=Xx,t.init=function(t,e,n){var i=$a(t);if(i)return i;var r=new Aa(t,e,n);return r.id="ec_"+u_++,s_[r.id]=r,Pn(t,c_,r.id),Ya(r),r},t.connect=function(t){if(y(t)){var e=t;t=null,Bx(e,function(e){null!=e.group&&(t=e.group)}),t=t||"g_"+h_++,Bx(e,function(e){e.group=t})}return l_[t]=!0,t},t.disConnect=qa,t.disconnect=f_,t.dispose=function(t){"string"==typeof t?t=s_[t]:t instanceof Aa||(t=$a(t)),t instanceof Aa&&!t.isDisposed()&&t.dispose()},t.getInstanceByDom=$a,t.getInstanceById=function(t){return s_[t]},t.registerTheme=Ka,t.registerPreprocessor=Qa,t.registerProcessor=Ja,t.registerPostUpdate=function(t){i_.push(t)},t.registerAction=ts,t.registerCoordinateSystem=function(t,e){yo.register(t,e)},t.getCoordinateSystemDimensions=function(t){var e=yo.get(t);if(e)return e.getDimensionsInfo?e.getDimensionsInfo():e.dimensions.slice()},t.registerLayout=es,t.registerVisual=ns,t.registerLoading=rs,t.extendComponentModel=os,t.extendComponentView=as,t.extendSeriesModel=ss,t.extendChartView=ls,t.setCanvasCreator=function(t){e("createCanvas",t)},t.registerMap=function(t,e,n){e.geoJson&&!e.features&&(n=e.specialAreas,e=e.geoJson),"string"==typeof e&&(e="undefined"!=typeof JSON&&JSON.parse?JSON.parse(e):new Function("return ("+e+");")()),d_[t]={geoJson:e,specialAreas:n}},t.getMap=function(t){return d_[t]},t.dataTool=p_,t.zrender=pm,t.graphic=ty,t.number=hy,t.format=yy,t.throttle=ua,t.helper=aw,t.matrix=qp,t.vector=Gp,t.color=dg,t.parseGeoJSON=lw,t.parseGeoJson=dw,t.util=fw,t.List=M_,t.Model=pr,t.Axis=cw,t.env=bp}); diff --git a/agent-analyer/src/main/resources/static/js/jquery-1.7.2.min.js b/agent-analyer/src/main/resources/static/js/jquery-1.7.2.min.js new file mode 100644 index 0000000..1775c9c --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/jquery-1.7.2.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.2 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( +a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f +.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/css/layui.css b/agent-analyer/src/main/resources/static/js/layui/css/layui.css new file mode 100644 index 0000000..4d95d18 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/css/layui.css @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + .layui-inline,img{display:inline-block;vertical-align:middle}.layui-rate,li{list-style:none}h1,h2,h3,h4,h5,h6{font-weight:400}.layui-edge,.layui-header,.layui-inline,.layui-main{position:relative}.layui-btn,.layui-edge,.layui-inline,img{vertical-align:middle}.layui-btn,.layui-disabled,.layui-icon,.layui-unselect{-webkit-user-select:none;-ms-user-select:none;-moz-user-select:none}blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}a:active,a:hover{outline:0}img{border:none}table{border-collapse:collapse;border-spacing:0}h4,h5,h6{font-size:100%}button,input,optgroup,option,select,textarea{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;outline:0}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}body{line-height:24px;font:14px Helvetica Neue,Helvetica,PingFang SC,\5FAE\8F6F\96C5\9ED1,Tahoma,Arial,sans-serif}hr{height:1px;margin:10px 0;border:0;clear:both}a{color:#333;text-decoration:none}a:hover{color:#777}a cite{font-style:normal;*cursor:pointer}.layui-border-box,.layui-border-box *{box-sizing:border-box}.layui-box,.layui-box *{box-sizing:content-box}.layui-clear{clear:both;*zoom:1}.layui-clear:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-inline{*display:inline;*zoom:1}.layui-edge{display:inline-block;width:0;height:0;border-width:6px;border-style:dashed;border-color:transparent;overflow:hidden}.layui-edge-top{top:-4px;border-bottom-color:#999;border-bottom-style:solid}.layui-edge-right{border-left-color:#999;border-left-style:solid}.layui-edge-bottom{top:2px;border-top-color:#999;border-top-style:solid}.layui-edge-left{border-right-color:#999;border-right-style:solid}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-disabled,.layui-disabled:hover{color:#d2d2d2!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=230);src:url(../font/iconfont.eot?v=230#iefix) format('embedded-opentype'),url(../font/iconfont.svg?v=230#iconfont) format('svg'),url(../font/iconfont.woff?v=230) format('woff'),url(../font/iconfont.ttf?v=230) format('truetype')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-icon-reply-fill:before{content:"\e611"}.layui-icon-set-fill:before{content:"\e614"}.layui-icon-menu-fill:before{content:"\e60f"}.layui-icon-search:before{content:"\e615"}.layui-icon-share:before{content:"\e641"}.layui-icon-set-sm:before{content:"\e620"}.layui-icon-engine:before{content:"\e628"}.layui-icon-close:before{content:"\1006"}.layui-icon-close-fill:before{content:"\1007"}.layui-icon-chart-screen:before{content:"\e629"}.layui-icon-star:before{content:"\e600"}.layui-icon-circle-dot:before{content:"\e617"}.layui-icon-chat:before{content:"\e606"}.layui-icon-release:before{content:"\e609"}.layui-icon-list:before{content:"\e60a"}.layui-icon-chart:before{content:"\e62c"}.layui-icon-ok-circle:before{content:"\1005"}.layui-icon-layim-theme:before{content:"\e61b"}.layui-icon-table:before{content:"\e62d"}.layui-icon-right:before{content:"\e602"}.layui-icon-left:before{content:"\e603"}.layui-icon-cart-simple:before{content:"\e698"}.layui-icon-face-cry:before{content:"\e69c"}.layui-icon-face-smile:before{content:"\e6af"}.layui-icon-survey:before{content:"\e6b2"}.layui-icon-tree:before{content:"\e62e"}.layui-icon-upload-circle:before{content:"\e62f"}.layui-icon-add-circle:before{content:"\e61f"}.layui-icon-download-circle:before{content:"\e601"}.layui-icon-templeate-1:before{content:"\e630"}.layui-icon-util:before{content:"\e631"}.layui-icon-face-surprised:before{content:"\e664"}.layui-icon-edit:before{content:"\e642"}.layui-icon-speaker:before{content:"\e645"}.layui-icon-down:before{content:"\e61a"}.layui-icon-file:before{content:"\e621"}.layui-icon-layouts:before{content:"\e632"}.layui-icon-rate-half:before{content:"\e6c9"}.layui-icon-add-circle-fine:before{content:"\e608"}.layui-icon-prev-circle:before{content:"\e633"}.layui-icon-read:before{content:"\e705"}.layui-icon-404:before{content:"\e61c"}.layui-icon-carousel:before{content:"\e634"}.layui-icon-help:before{content:"\e607"}.layui-icon-code-circle:before{content:"\e635"}.layui-icon-water:before{content:"\e636"}.layui-icon-username:before{content:"\e66f"}.layui-icon-find-fill:before{content:"\e670"}.layui-icon-about:before{content:"\e60b"}.layui-icon-location:before{content:"\e715"}.layui-icon-up:before{content:"\e619"}.layui-icon-pause:before{content:"\e651"}.layui-icon-date:before{content:"\e637"}.layui-icon-layim-uploadfile:before{content:"\e61d"}.layui-icon-delete:before{content:"\e640"}.layui-icon-play:before{content:"\e652"}.layui-icon-top:before{content:"\e604"}.layui-icon-friends:before{content:"\e612"}.layui-icon-refresh-3:before{content:"\e9aa"}.layui-icon-ok:before{content:"\e605"}.layui-icon-layer:before{content:"\e638"}.layui-icon-face-smile-fine:before{content:"\e60c"}.layui-icon-dollar:before{content:"\e659"}.layui-icon-group:before{content:"\e613"}.layui-icon-layim-download:before{content:"\e61e"}.layui-icon-picture-fine:before{content:"\e60d"}.layui-icon-link:before{content:"\e64c"}.layui-icon-diamond:before{content:"\e735"}.layui-icon-log:before{content:"\e60e"}.layui-icon-rate-solid:before{content:"\e67a"}.layui-icon-fonts-del:before{content:"\e64f"}.layui-icon-unlink:before{content:"\e64d"}.layui-icon-fonts-clear:before{content:"\e639"}.layui-icon-triangle-r:before{content:"\e623"}.layui-icon-circle:before{content:"\e63f"}.layui-icon-radio:before{content:"\e643"}.layui-icon-align-center:before{content:"\e647"}.layui-icon-align-right:before{content:"\e648"}.layui-icon-align-left:before{content:"\e649"}.layui-icon-loading-1:before{content:"\e63e"}.layui-icon-return:before{content:"\e65c"}.layui-icon-fonts-strong:before{content:"\e62b"}.layui-icon-upload:before{content:"\e67c"}.layui-icon-dialogue:before{content:"\e63a"}.layui-icon-video:before{content:"\e6ed"}.layui-icon-headset:before{content:"\e6fc"}.layui-icon-cellphone-fine:before{content:"\e63b"}.layui-icon-add-1:before{content:"\e654"}.layui-icon-face-smile-b:before{content:"\e650"}.layui-icon-fonts-html:before{content:"\e64b"}.layui-icon-form:before{content:"\e63c"}.layui-icon-cart:before{content:"\e657"}.layui-icon-camera-fill:before{content:"\e65d"}.layui-icon-tabs:before{content:"\e62a"}.layui-icon-fonts-code:before{content:"\e64e"}.layui-icon-fire:before{content:"\e756"}.layui-icon-set:before{content:"\e716"}.layui-icon-fonts-u:before{content:"\e646"}.layui-icon-triangle-d:before{content:"\e625"}.layui-icon-tips:before{content:"\e702"}.layui-icon-picture:before{content:"\e64a"}.layui-icon-more-vertical:before{content:"\e671"}.layui-icon-flag:before{content:"\e66c"}.layui-icon-loading:before{content:"\e63d"}.layui-icon-fonts-i:before{content:"\e644"}.layui-icon-refresh-1:before{content:"\e666"}.layui-icon-rmb:before{content:"\e65e"}.layui-icon-home:before{content:"\e68e"}.layui-icon-user:before{content:"\e770"}.layui-icon-notice:before{content:"\e667"}.layui-icon-login-weibo:before{content:"\e675"}.layui-icon-voice:before{content:"\e688"}.layui-icon-upload-drag:before{content:"\e681"}.layui-icon-login-qq:before{content:"\e676"}.layui-icon-snowflake:before{content:"\e6b1"}.layui-icon-file-b:before{content:"\e655"}.layui-icon-template:before{content:"\e663"}.layui-icon-auz:before{content:"\e672"}.layui-icon-console:before{content:"\e665"}.layui-icon-app:before{content:"\e653"}.layui-icon-prev:before{content:"\e65a"}.layui-icon-website:before{content:"\e7ae"}.layui-icon-next:before{content:"\e65b"}.layui-icon-component:before{content:"\e857"}.layui-icon-more:before{content:"\e65f"}.layui-icon-login-wechat:before{content:"\e677"}.layui-icon-shrink-right:before{content:"\e668"}.layui-icon-spread-left:before{content:"\e66b"}.layui-icon-camera:before{content:"\e660"}.layui-icon-note:before{content:"\e66e"}.layui-icon-refresh:before{content:"\e669"}.layui-icon-female:before{content:"\e661"}.layui-icon-male:before{content:"\e662"}.layui-icon-password:before{content:"\e673"}.layui-icon-senior:before{content:"\e674"}.layui-icon-theme:before{content:"\e66a"}.layui-icon-tread:before{content:"\e6c5"}.layui-icon-praise:before{content:"\e6c6"}.layui-icon-star-fill:before{content:"\e658"}.layui-icon-rate:before{content:"\e67b"}.layui-icon-template-1:before{content:"\e656"}.layui-icon-vercode:before{content:"\e679"}.layui-icon-cellphone:before{content:"\e678"}.layui-icon-screen-full:before{content:"\e622"}.layui-icon-screen-restore:before{content:"\e758"}.layui-main{width:1140px;margin:0 auto}.layui-header{z-index:1000;height:60px}.layui-header a:hover{transition:all .5s;-webkit-transition:all .5s}.layui-side{position:fixed;left:0;top:0;bottom:0;z-index:999;width:200px;overflow-x:hidden}.layui-side-scroll{position:relative;width:220px;height:100%;overflow-x:hidden}.layui-body{position:absolute;left:200px;right:0;top:0;bottom:0;z-index:998;width:auto;overflow:hidden;overflow-y:auto;box-sizing:border-box}.layui-layout-body{overflow:hidden}.layui-layout-admin .layui-header{background-color:#23262E}.layui-layout-admin .layui-side{top:60px;width:200px;overflow-x:hidden}.layui-layout-admin .layui-body{top:60px;bottom:44px}.layui-layout-admin .layui-main{width:auto;margin:0 15px}.layui-layout-admin .layui-footer{position:fixed;left:200px;right:0;bottom:0;height:44px;line-height:44px;padding:0 15px;background-color:#eee}.layui-layout-admin .layui-logo{position:absolute;left:0;top:0;width:200px;height:100%;line-height:60px;text-align:center;color:#009688;font-size:16px}.layui-layout-admin .layui-header .layui-nav{background:0 0}.layui-layout-left{position:absolute!important;left:200px;top:0}.layui-layout-right{position:absolute!important;right:0;top:0}.layui-container{position:relative;margin:0 auto;padding:0 15px;box-sizing:border-box}.layui-fluid{position:relative;margin:0 auto;padding:0 15px}.layui-row:after,.layui-row:before{content:'';display:block;clear:both}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9,.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9,.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9,.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{position:relative;display:block;box-sizing:border-box}.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{float:left}.layui-col-xs1{width:8.33333333%}.layui-col-xs2{width:16.66666667%}.layui-col-xs3{width:25%}.layui-col-xs4{width:33.33333333%}.layui-col-xs5{width:41.66666667%}.layui-col-xs6{width:50%}.layui-col-xs7{width:58.33333333%}.layui-col-xs8{width:66.66666667%}.layui-col-xs9{width:75%}.layui-col-xs10{width:83.33333333%}.layui-col-xs11{width:91.66666667%}.layui-col-xs12{width:100%}.layui-col-xs-offset1{margin-left:8.33333333%}.layui-col-xs-offset2{margin-left:16.66666667%}.layui-col-xs-offset3{margin-left:25%}.layui-col-xs-offset4{margin-left:33.33333333%}.layui-col-xs-offset5{margin-left:41.66666667%}.layui-col-xs-offset6{margin-left:50%}.layui-col-xs-offset7{margin-left:58.33333333%}.layui-col-xs-offset8{margin-left:66.66666667%}.layui-col-xs-offset9{margin-left:75%}.layui-col-xs-offset10{margin-left:83.33333333%}.layui-col-xs-offset11{margin-left:91.66666667%}.layui-col-xs-offset12{margin-left:100%}@media screen and (max-width:768px){.layui-hide-xs{display:none!important}.layui-show-xs-block{display:block!important}.layui-show-xs-inline{display:inline!important}.layui-show-xs-inline-block{display:inline-block!important}}@media screen and (min-width:768px){.layui-container{width:750px}.layui-hide-sm{display:none!important}.layui-show-sm-block{display:block!important}.layui-show-sm-inline{display:inline!important}.layui-show-sm-inline-block{display:inline-block!important}.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9{float:left}.layui-col-sm1{width:8.33333333%}.layui-col-sm2{width:16.66666667%}.layui-col-sm3{width:25%}.layui-col-sm4{width:33.33333333%}.layui-col-sm5{width:41.66666667%}.layui-col-sm6{width:50%}.layui-col-sm7{width:58.33333333%}.layui-col-sm8{width:66.66666667%}.layui-col-sm9{width:75%}.layui-col-sm10{width:83.33333333%}.layui-col-sm11{width:91.66666667%}.layui-col-sm12{width:100%}.layui-col-sm-offset1{margin-left:8.33333333%}.layui-col-sm-offset2{margin-left:16.66666667%}.layui-col-sm-offset3{margin-left:25%}.layui-col-sm-offset4{margin-left:33.33333333%}.layui-col-sm-offset5{margin-left:41.66666667%}.layui-col-sm-offset6{margin-left:50%}.layui-col-sm-offset7{margin-left:58.33333333%}.layui-col-sm-offset8{margin-left:66.66666667%}.layui-col-sm-offset9{margin-left:75%}.layui-col-sm-offset10{margin-left:83.33333333%}.layui-col-sm-offset11{margin-left:91.66666667%}.layui-col-sm-offset12{margin-left:100%}}@media screen and (min-width:992px){.layui-container{width:970px}.layui-hide-md{display:none!important}.layui-show-md-block{display:block!important}.layui-show-md-inline{display:inline!important}.layui-show-md-inline-block{display:inline-block!important}.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9{float:left}.layui-col-md1{width:8.33333333%}.layui-col-md2{width:16.66666667%}.layui-col-md3{width:25%}.layui-col-md4{width:33.33333333%}.layui-col-md5{width:41.66666667%}.layui-col-md6{width:50%}.layui-col-md7{width:58.33333333%}.layui-col-md8{width:66.66666667%}.layui-col-md9{width:75%}.layui-col-md10{width:83.33333333%}.layui-col-md11{width:91.66666667%}.layui-col-md12{width:100%}.layui-col-md-offset1{margin-left:8.33333333%}.layui-col-md-offset2{margin-left:16.66666667%}.layui-col-md-offset3{margin-left:25%}.layui-col-md-offset4{margin-left:33.33333333%}.layui-col-md-offset5{margin-left:41.66666667%}.layui-col-md-offset6{margin-left:50%}.layui-col-md-offset7{margin-left:58.33333333%}.layui-col-md-offset8{margin-left:66.66666667%}.layui-col-md-offset9{margin-left:75%}.layui-col-md-offset10{margin-left:83.33333333%}.layui-col-md-offset11{margin-left:91.66666667%}.layui-col-md-offset12{margin-left:100%}}@media screen and (min-width:1200px){.layui-container{width:1170px}.layui-hide-lg{display:none!important}.layui-show-lg-block{display:block!important}.layui-show-lg-inline{display:inline!important}.layui-show-lg-inline-block{display:inline-block!important}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9{float:left}.layui-col-lg1{width:8.33333333%}.layui-col-lg2{width:16.66666667%}.layui-col-lg3{width:25%}.layui-col-lg4{width:33.33333333%}.layui-col-lg5{width:41.66666667%}.layui-col-lg6{width:50%}.layui-col-lg7{width:58.33333333%}.layui-col-lg8{width:66.66666667%}.layui-col-lg9{width:75%}.layui-col-lg10{width:83.33333333%}.layui-col-lg11{width:91.66666667%}.layui-col-lg12{width:100%}.layui-col-lg-offset1{margin-left:8.33333333%}.layui-col-lg-offset2{margin-left:16.66666667%}.layui-col-lg-offset3{margin-left:25%}.layui-col-lg-offset4{margin-left:33.33333333%}.layui-col-lg-offset5{margin-left:41.66666667%}.layui-col-lg-offset6{margin-left:50%}.layui-col-lg-offset7{margin-left:58.33333333%}.layui-col-lg-offset8{margin-left:66.66666667%}.layui-col-lg-offset9{margin-left:75%}.layui-col-lg-offset10{margin-left:83.33333333%}.layui-col-lg-offset11{margin-left:91.66666667%}.layui-col-lg-offset12{margin-left:100%}}.layui-col-space1{margin:-.5px}.layui-col-space1>*{padding:.5px}.layui-col-space3{margin:-1.5px}.layui-col-space3>*{padding:1.5px}.layui-col-space5{margin:-2.5px}.layui-col-space5>*{padding:2.5px}.layui-col-space8{margin:-3.5px}.layui-col-space8>*{padding:3.5px}.layui-col-space10{margin:-5px}.layui-col-space10>*{padding:5px}.layui-col-space12{margin:-6px}.layui-col-space12>*{padding:6px}.layui-col-space15{margin:-7.5px}.layui-col-space15>*{padding:7.5px}.layui-col-space18{margin:-9px}.layui-col-space18>*{padding:9px}.layui-col-space20{margin:-10px}.layui-col-space20>*{padding:10px}.layui-col-space22{margin:-11px}.layui-col-space22>*{padding:11px}.layui-col-space25{margin:-12.5px}.layui-col-space25>*{padding:12.5px}.layui-col-space30{margin:-15px}.layui-col-space30>*{padding:15px}.layui-btn,.layui-input,.layui-select,.layui-textarea,.layui-upload-button{outline:0;-webkit-appearance:none;transition:all .3s;-webkit-transition:all .3s;box-sizing:border-box}.layui-elem-quote{margin-bottom:10px;padding:15px;line-height:22px;border-left:5px solid #009688;border-radius:0 2px 2px 0;background-color:#f2f2f2}.layui-quote-nm{border-style:solid;border-width:1px 1px 1px 5px;background:0 0}.layui-elem-field{margin-bottom:10px;padding:0;border-width:1px;border-style:solid}.layui-elem-field legend{margin-left:20px;padding:0 10px;font-size:20px;font-weight:300}.layui-field-title{margin:10px 0 20px;border-width:1px 0 0}.layui-field-box{padding:10px 15px}.layui-field-title .layui-field-box{padding:10px 0}.layui-progress{position:relative;height:6px;border-radius:20px;background-color:#e2e2e2}.layui-progress-bar{position:absolute;left:0;top:0;width:0;max-width:100%;height:6px;border-radius:20px;text-align:right;background-color:#5FB878;transition:all .3s;-webkit-transition:all .3s}.layui-progress-big,.layui-progress-big .layui-progress-bar{height:18px;line-height:18px}.layui-progress-text{position:relative;top:-20px;line-height:18px;font-size:12px;color:#666}.layui-progress-big .layui-progress-text{position:static;padding:0 10px;color:#fff}.layui-collapse{border-width:1px;border-style:solid;border-radius:2px}.layui-colla-content,.layui-colla-item{border-top-width:1px;border-top-style:solid}.layui-colla-item:first-child{border-top:none}.layui-colla-title{position:relative;height:42px;line-height:42px;padding:0 15px 0 35px;color:#333;background-color:#f2f2f2;cursor:pointer;font-size:14px;overflow:hidden}.layui-colla-content{display:none;padding:10px 15px;line-height:22px;color:#666}.layui-colla-icon{position:absolute;left:15px;top:0;font-size:14px}.layui-card-body,.layui-card-header,.layui-form-label,.layui-form-mid,.layui-form-select,.layui-input-block,.layui-input-inline,.layui-textarea{position:relative}.layui-card{margin-bottom:15px;border-radius:2px;background-color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.layui-card:last-child{margin-bottom:0}.layui-card-header{height:42px;line-height:42px;padding:0 15px;border-bottom:1px solid #f6f6f6;color:#333;border-radius:2px 2px 0 0;font-size:14px}.layui-bg-black,.layui-bg-blue,.layui-bg-cyan,.layui-bg-green,.layui-bg-orange,.layui-bg-red{color:#fff!important}.layui-card-body{padding:10px 15px;line-height:24px}.layui-card-body[pad15]{padding:15px}.layui-card-body[pad20]{padding:20px}.layui-card-body .layui-table{margin:5px 0}.layui-card .layui-tab{margin:0}.layui-panel-window{position:relative;padding:15px;border-radius:0;border-top:5px solid #E6E6E6;background-color:#fff}.layui-bg-red{background-color:#FF5722!important}.layui-bg-orange{background-color:#FFB800!important}.layui-bg-green{background-color:#009688!important}.layui-bg-cyan{background-color:#2F4056!important}.layui-bg-blue{background-color:#1E9FFF!important}.layui-bg-black{background-color:#393D49!important}.layui-bg-gray{background-color:#eee!important;color:#666!important}.layui-badge-rim,.layui-colla-content,.layui-colla-item,.layui-collapse,.layui-elem-field,.layui-form-pane .layui-form-item[pane],.layui-form-pane .layui-form-label,.layui-input,.layui-layedit,.layui-layedit-tool,.layui-quote-nm,.layui-select,.layui-tab-bar,.layui-tab-card,.layui-tab-title,.layui-tab-title .layui-this:after,.layui-textarea{border-color:#e6e6e6}.layui-timeline-item:before,hr{background-color:#e6e6e6}.layui-text{line-height:22px;font-size:14px;color:#666}.layui-text h1,.layui-text h2,.layui-text h3{font-weight:500;color:#333}.layui-text h1{font-size:30px}.layui-text h2{font-size:24px}.layui-text h3{font-size:18px}.layui-text a:not(.layui-btn){color:#01AAED}.layui-text a:not(.layui-btn):hover{text-decoration:underline}.layui-text ul{padding:5px 0 5px 15px}.layui-text ul li{margin-top:5px;list-style-type:disc}.layui-text em,.layui-word-aux{color:#999!important;padding:0 5px!important}.layui-btn{display:inline-block;height:38px;line-height:38px;padding:0 18px;background-color:#009688;color:#fff;white-space:nowrap;text-align:center;font-size:14px;border:none;border-radius:2px;cursor:pointer}.layui-btn:hover{opacity:.8;filter:alpha(opacity=80);color:#fff}.layui-btn:active{opacity:1;filter:alpha(opacity=100)}.layui-btn+.layui-btn{margin-left:10px}.layui-btn-container{font-size:0}.layui-btn-container .layui-btn{margin-right:10px;margin-bottom:10px}.layui-btn-container .layui-btn+.layui-btn{margin-left:0}.layui-table .layui-btn-container .layui-btn{margin-bottom:9px}.layui-btn-radius{border-radius:100px}.layui-btn .layui-icon{margin-right:3px;font-size:18px;vertical-align:bottom;vertical-align:middle\9}.layui-btn-primary{border:1px solid #C9C9C9;background-color:#fff;color:#555}.layui-btn-primary:hover{border-color:#009688;color:#333}.layui-btn-normal{background-color:#1E9FFF}.layui-btn-warm{background-color:#FFB800}.layui-btn-danger{background-color:#FF5722}.layui-btn-disabled,.layui-btn-disabled:active,.layui-btn-disabled:hover{border:1px solid #e6e6e6;background-color:#FBFBFB;color:#C9C9C9;cursor:not-allowed;opacity:1}.layui-btn-lg{height:44px;line-height:44px;padding:0 25px;font-size:16px}.layui-btn-sm{height:30px;line-height:30px;padding:0 10px;font-size:12px}.layui-btn-sm i{font-size:16px!important}.layui-btn-xs{height:22px;line-height:22px;padding:0 5px;font-size:12px}.layui-btn-xs i{font-size:14px!important}.layui-btn-group{display:inline-block;vertical-align:middle;font-size:0}.layui-btn-group .layui-btn{margin-left:0!important;margin-right:0!important;border-left:1px solid rgba(255,255,255,.5);border-radius:0}.layui-btn-group .layui-btn-primary{border-left:none}.layui-btn-group .layui-btn-primary:hover{border-color:#C9C9C9;color:#009688}.layui-btn-group .layui-btn:first-child{border-left:none;border-radius:2px 0 0 2px}.layui-btn-group .layui-btn-primary:first-child{border-left:1px solid #c9c9c9}.layui-btn-group .layui-btn:last-child{border-radius:0 2px 2px 0}.layui-btn-group .layui-btn+.layui-btn{margin-left:0}.layui-btn-group+.layui-btn-group{margin-left:10px}.layui-btn-fluid{width:100%}.layui-input,.layui-select,.layui-textarea{height:38px;line-height:1.3;line-height:38px\9;border-width:1px;border-style:solid;background-color:#fff;border-radius:2px}.layui-input::-webkit-input-placeholder,.layui-select::-webkit-input-placeholder,.layui-textarea::-webkit-input-placeholder{line-height:1.3}.layui-input,.layui-textarea{display:block;width:100%;padding-left:10px}.layui-input:hover,.layui-textarea:hover{border-color:#D2D2D2!important}.layui-input:focus,.layui-textarea:focus{border-color:#C9C9C9!important}.layui-textarea{min-height:100px;height:auto;line-height:20px;padding:6px 10px;resize:vertical}.layui-select{padding:0 10px}.layui-form input[type=checkbox],.layui-form input[type=radio],.layui-form select{display:none}.layui-form [lay-ignore]{display:initial}.layui-form-item{margin-bottom:15px;clear:both;*zoom:1}.layui-form-item:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-form-label{float:left;display:block;padding:9px 15px;width:80px;font-weight:400;line-height:20px;text-align:right}.layui-form-label-col{display:block;float:none;padding:9px 0;line-height:20px;text-align:left}.layui-form-item .layui-inline{margin-bottom:5px;margin-right:10px}.layui-input-block{margin-left:110px;min-height:36px}.layui-input-inline{display:inline-block;vertical-align:middle}.layui-form-item .layui-input-inline{float:left;width:190px;margin-right:10px}.layui-form-text .layui-input-inline{width:auto}.layui-form-mid{float:left;display:block;padding:9px 0!important;line-height:20px;margin-right:10px}.layui-form-danger+.layui-form-select .layui-input,.layui-form-danger:focus{border-color:#FF5722!important}.layui-form-select .layui-input{padding-right:30px;cursor:pointer}.layui-form-select .layui-edge{position:absolute;right:10px;top:50%;margin-top:-3px;cursor:pointer;border-width:6px;border-top-color:#c2c2c2;border-top-style:solid;transition:all .3s;-webkit-transition:all .3s}.layui-form-select dl{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:999;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box}.layui-form-select dl dd,.layui-form-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.layui-form-select dl dt{font-size:12px;color:#999}.layui-form-select dl dd{cursor:pointer}.layui-form-select dl dd:hover{background-color:#f2f2f2}.layui-form-select .layui-select-group dd{padding-left:20px}.layui-form-select dl dd.layui-select-tips{padding-left:10px!important;color:#999}.layui-form-select dl dd.layui-this{background-color:#5FB878;color:#fff}.layui-form-checkbox,.layui-form-select dl dd.layui-disabled{background-color:#fff}.layui-form-selected dl{display:block}.layui-form-checkbox,.layui-form-checkbox *,.layui-form-switch{display:inline-block;vertical-align:middle}.layui-form-selected .layui-edge{margin-top:-9px;-webkit-transform:rotate(180deg);transform:rotate(180deg);margin-top:-3px\9}:root .layui-form-selected .layui-edge{margin-top:-9px\0/IE9}.layui-form-selectup dl{top:auto;bottom:42px}.layui-select-none{margin:5px 0;text-align:center;color:#999}.layui-select-disabled .layui-disabled{border-color:#eee!important}.layui-select-disabled .layui-edge{border-top-color:#d2d2d2}.layui-form-checkbox{position:relative;height:30px;line-height:30px;margin-right:10px;padding-right:30px;cursor:pointer;font-size:0;-webkit-transition:.1s linear;transition:.1s linear;box-sizing:border-box}.layui-form-checkbox span{padding:0 10px;height:100%;font-size:14px;border-radius:2px 0 0 2px;background-color:#d2d2d2;color:#fff;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.layui-form-checkbox:hover span{background-color:#c2c2c2}.layui-form-checkbox i{position:absolute;right:0;top:0;width:30px;height:28px;border:1px solid #d2d2d2;border-left:none;border-radius:0 2px 2px 0;color:#fff;font-size:20px;text-align:center}.layui-form-checkbox:hover i{border-color:#c2c2c2;color:#c2c2c2}.layui-form-checked,.layui-form-checked:hover{border-color:#5FB878}.layui-form-checked span,.layui-form-checked:hover span{background-color:#5FB878}.layui-form-checked i,.layui-form-checked:hover i{color:#5FB878}.layui-form-item .layui-form-checkbox{margin-top:4px}.layui-form-checkbox[lay-skin=primary]{height:auto!important;line-height:normal!important;border:none!important;margin-right:0;padding-right:0;background:0 0}.layui-form-checkbox[lay-skin=primary] span{float:right;padding-right:15px;line-height:18px;background:0 0;color:#666}.layui-form-checkbox[lay-skin=primary] i{position:relative;top:0;width:16px;height:16px;line-height:16px;border:1px solid #d2d2d2;font-size:12px;border-radius:2px;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-checkbox[lay-skin=primary]:hover i{border-color:#5FB878;color:#fff}.layui-form-checked[lay-skin=primary] i{border-color:#5FB878;background-color:#5FB878;color:#fff}.layui-checkbox-disbaled[lay-skin=primary] span{background:0 0!important;color:#c2c2c2}.layui-checkbox-disbaled[lay-skin=primary]:hover i{border-color:#d2d2d2}.layui-form-item .layui-form-checkbox[lay-skin=primary]{margin-top:10px}.layui-form-switch{position:relative;height:22px;line-height:22px;min-width:35px;padding:0 5px;margin-top:8px;border:1px solid #d2d2d2;border-radius:20px;cursor:pointer;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch i{position:absolute;left:5px;top:3px;width:16px;height:16px;border-radius:20px;background-color:#d2d2d2;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch em{position:relative;top:0;width:25px;margin-left:21px;padding:0!important;text-align:center!important;color:#999!important;font-style:normal!important;font-size:12px}.layui-form-onswitch{border-color:#5FB878;background-color:#5FB878}.layui-checkbox-disbaled,.layui-checkbox-disbaled i{border-color:#e2e2e2!important}.layui-form-onswitch i{left:100%;margin-left:-21px;background-color:#fff}.layui-form-onswitch em{margin-left:5px;margin-right:21px;color:#fff!important}.layui-checkbox-disbaled span{background-color:#e2e2e2!important}.layui-checkbox-disbaled:hover i{color:#fff!important}[lay-radio]{display:none}.layui-form-radio,.layui-form-radio *{display:inline-block;vertical-align:middle}.layui-form-radio{line-height:28px;margin:6px 10px 0 0;padding-right:10px;cursor:pointer;font-size:0}.layui-form-radio *{font-size:14px}.layui-form-radio>i{margin-right:8px;font-size:22px;color:#c2c2c2}.layui-form-radio>i:hover,.layui-form-radioed>i{color:#5FB878}.layui-radio-disbaled>i{color:#e2e2e2!important}.layui-form-pane .layui-form-label{width:110px;padding:8px 15px;height:38px;line-height:20px;border-width:1px;border-style:solid;border-radius:2px 0 0 2px;text-align:center;background-color:#FBFBFB;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;box-sizing:border-box}.layui-form-pane .layui-input-inline{margin-left:-1px}.layui-form-pane .layui-input-block{margin-left:110px;left:-1px}.layui-form-pane .layui-input{border-radius:0 2px 2px 0}.layui-form-pane .layui-form-text .layui-form-label{float:none;width:100%;border-radius:2px;box-sizing:border-box;text-align:left}.layui-form-pane .layui-form-text .layui-input-inline{display:block;margin:0;top:-1px;clear:both}.layui-form-pane .layui-form-text .layui-input-block{margin:0;left:0;top:-1px}.layui-form-pane .layui-form-text .layui-textarea{min-height:100px;border-radius:0 0 2px 2px}.layui-form-pane .layui-form-checkbox{margin:4px 0 4px 10px}.layui-form-pane .layui-form-radio,.layui-form-pane .layui-form-switch{margin-top:6px;margin-left:10px}.layui-form-pane .layui-form-item[pane]{position:relative;border-width:1px;border-style:solid}.layui-form-pane .layui-form-item[pane] .layui-form-label{position:absolute;left:0;top:0;height:100%;border-width:0 1px 0 0}.layui-form-pane .layui-form-item[pane] .layui-input-inline{margin-left:110px}@media screen and (max-width:450px){.layui-form-item .layui-form-label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-form-item .layui-inline{display:block;margin-right:0;margin-bottom:20px;clear:both}.layui-form-item .layui-inline:after{content:'\20';clear:both;display:block;height:0}.layui-form-item .layui-input-inline{display:block;float:none;left:-3px;width:auto;margin:0 0 10px 112px}.layui-form-item .layui-input-inline+.layui-form-mid{margin-left:110px;top:-5px;padding:0}.layui-form-item .layui-form-checkbox{margin-right:5px;margin-bottom:5px}}.layui-layedit{border-width:1px;border-style:solid;border-radius:2px}.layui-layedit-tool{padding:3px 5px;border-bottom-width:1px;border-bottom-style:solid;font-size:0}.layedit-tool-fixed{position:fixed;top:0;border-top:1px solid #e2e2e2}.layui-layedit-tool .layedit-tool-mid,.layui-layedit-tool .layui-icon{display:inline-block;vertical-align:middle;text-align:center;font-size:14px}.layui-layedit-tool .layui-icon{position:relative;width:32px;height:30px;line-height:30px;margin:3px 5px;color:#777;cursor:pointer;border-radius:2px}.layui-layedit-tool .layui-icon:hover{color:#393D49}.layui-layedit-tool .layui-icon:active{color:#000}.layui-layedit-tool .layedit-tool-active{background-color:#e2e2e2;color:#000}.layui-layedit-tool .layui-disabled,.layui-layedit-tool .layui-disabled:hover{color:#d2d2d2;cursor:not-allowed}.layui-layedit-tool .layedit-tool-mid{width:1px;height:18px;margin:0 10px;background-color:#d2d2d2}.layedit-tool-html{width:50px!important;font-size:30px!important}.layedit-tool-b,.layedit-tool-code,.layedit-tool-help{font-size:16px!important}.layedit-tool-d,.layedit-tool-face,.layedit-tool-image,.layedit-tool-unlink{font-size:18px!important}.layedit-tool-image input{position:absolute;font-size:0;left:0;top:0;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-layedit-iframe iframe{display:block;width:100%}#LAY_layedit_code{overflow:hidden}.layui-laypage{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;margin:10px 0;font-size:0}.layui-laypage>a:first-child,.layui-laypage>a:first-child em{border-radius:2px 0 0 2px}.layui-laypage>a:last-child,.layui-laypage>a:last-child em{border-radius:0 2px 2px 0}.layui-laypage>:first-child{margin-left:0!important}.layui-laypage>:last-child{margin-right:0!important}.layui-laypage a,.layui-laypage button,.layui-laypage input,.layui-laypage select,.layui-laypage span{border:1px solid #e2e2e2}.layui-laypage a,.layui-laypage span{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding:0 15px;height:28px;line-height:28px;margin:0 -1px 5px 0;background-color:#fff;color:#333;font-size:12px}.layui-laypage a:hover{color:#009688}.layui-laypage em{font-style:normal}.layui-laypage .layui-laypage-spr{color:#999;font-weight:700}.layui-laypage a{text-decoration:none}.layui-laypage .layui-laypage-curr{position:relative}.layui-laypage .layui-laypage-curr em{position:relative;color:#fff}.layui-laypage .layui-laypage-curr .layui-laypage-em{position:absolute;left:-1px;top:-1px;padding:1px;width:100%;height:100%;background-color:#009688}.layui-laypage-em{border-radius:2px}.layui-laypage-next em,.layui-laypage-prev em{font-family:Sim sun;font-size:16px}.layui-laypage .layui-laypage-count,.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh,.layui-laypage .layui-laypage-skip{margin-left:10px;margin-right:10px;padding:0;border:none}.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh{vertical-align:top}.layui-laypage .layui-laypage-refresh i{font-size:18px;cursor:pointer}.layui-laypage select{height:22px;padding:3px;border-radius:2px;cursor:pointer}.layui-laypage .layui-laypage-skip{height:30px;line-height:30px;color:#999}.layui-laypage button,.layui-laypage input{height:30px;line-height:30px;border-radius:2px;vertical-align:top;background-color:#fff;box-sizing:border-box}.layui-laypage input{display:inline-block;width:40px;margin:0 10px;padding:0 3px;text-align:center}.layui-laypage input:focus,.layui-laypage select:focus{border-color:#009688!important}.layui-laypage button{margin-left:10px;padding:0 10px;cursor:pointer}.layui-table,.layui-table-view{margin:10px 0}.layui-flow-more{margin:10px 0;text-align:center;color:#999;font-size:14px}.layui-flow-more a{height:32px;line-height:32px}.layui-flow-more a *{display:inline-block;vertical-align:top}.layui-flow-more a cite{padding:0 20px;border-radius:3px;background-color:#eee;color:#333;font-style:normal}.layui-flow-more a cite:hover{opacity:.8}.layui-flow-more a i{font-size:30px;color:#737383}.layui-table{width:80%;background-color:#fff;color:#666}.layui-table tr{transition:all .3s;-webkit-transition:all .3s}.layui-table th{text-align:left;font-weight:400}.layui-table tbody tr:hover,.layui-table thead tr,.layui-table-click,.layui-table-header,.layui-table-hover,.layui-table-mend,.layui-table-patch,.layui-table-tool,.layui-table[lay-even] tr:nth-child(even){background-color:#f2f2f2}.layui-table td,.layui-table th,.layui-table-fixed-r,.layui-table-header,.layui-table-page,.layui-table-tips-main,.layui-table-tool,.layui-table-view,.layui-table[lay-skin=line],.layui-table[lay-skin=row]{border-width:1px;border-style:solid;border-color:#e6e6e6}.layui-table td,.layui-table th{position:relative;padding:9px 15px;min-height:20px;line-height:20px;font-size:14px}.layui-table[lay-skin=line] td,.layui-table[lay-skin=line] th{border-width:0 0 1px}.layui-table[lay-skin=row] td,.layui-table[lay-skin=row] th{border-width:0 1px 0 0}.layui-table[lay-skin=nob] td,.layui-table[lay-skin=nob] th{border:none}.layui-table img{max-width:100px}.layui-table[lay-size=lg] td,.layui-table[lay-size=lg] th{padding:15px 30px}.layui-table-view .layui-table[lay-size=lg] .layui-table-cell{height:40px;line-height:40px}.layui-table[lay-size=sm] td,.layui-table[lay-size=sm] th{font-size:12px;padding:5px 10px}.layui-table-view .layui-table[lay-size=sm] .layui-table-cell{height:20px;line-height:20px}.layui-table[lay-data]{display:none}.layui-table-box,.layui-table-view{position:relative;}.layui-table-view .layui-table{position:relative;width:auto;margin:0}.layui-table-body,.layui-table-header .layui-table,.layui-table-page{margin-bottom:-1px}.layui-table-view .layui-table[lay-skin=line]{border-width:0 1px 0 0}.layui-table-view .layui-table[lay-skin=row]{border-width:0 0 1px}.layui-table-view .layui-table td,.layui-table-view .layui-table th{padding:5px 0;border-top:none;border-left:none}.layui-table-view .layui-table td{cursor:default}.layui-table-view .layui-form-checkbox[lay-skin=primary] i{width:18px;height:18px}.layui-table-header{border-width:0 0 1px;overflow:hidden}.layui-table-sort{width:10px;height:20px;margin-left:5px;cursor:pointer!important}.layui-table-sort .layui-edge{position:absolute;left:5px;border-width:5px}.layui-table-sort .layui-table-sort-asc{top:4px;border-top:none;border-bottom-style:solid;border-bottom-color:#b2b2b2}.layui-table-sort .layui-table-sort-asc:hover{border-bottom-color:#666}.layui-table-sort .layui-table-sort-desc{bottom:4px;border-bottom:none;border-top-style:solid;border-top-color:#b2b2b2}.layui-table-sort .layui-table-sort-desc:hover{border-top-color:#666}.layui-table-sort[lay-sort=asc] .layui-table-sort-asc{border-bottom-color:#000}.layui-table-sort[lay-sort=desc] .layui-table-sort-desc{border-top-color:#000}.layui-table-cell{height:28px;line-height:28px;padding:0 15px;position:relative;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;box-sizing:border-box}.layui-table-cell .layui-form-checkbox[lay-skin=primary],.layui-table-cell .layui-form-radio[lay-skin=primary]{top:-1px;vertical-align:middle}.layui-table-cell .layui-form-radio{padding-right:0}.layui-table-cell .layui-form-radio>i{margin-right:0}.layui-table-cell .layui-table-link{color:#01AAED}.laytable-cell-checkbox,.laytable-cell-numbers,.laytable-cell-radio,.laytable-cell-space{padding:0;text-align:center}.layui-table-body{position:relative;overflow:auto;margin-right:-1px}.layui-table-body .layui-none{line-height:40px;text-align:center;color:#999}.layui-table-fixed{position:absolute;left:0;top:0}.layui-table-fixed .layui-table-body{overflow:hidden}.layui-table-fixed-l{box-shadow:0 -1px 8px rgba(0,0,0,.08)}.layui-table-fixed-r{left:auto;right:-1px;border-width:0 0 0 1px;box-shadow:-1px 0 8px rgba(0,0,0,.08)}.layui-table-fixed-r .layui-table-header{position:relative;overflow:visible}.layui-table-mend{position:absolute;right:-49px;top:0;height:100%;width:50px}.layui-table-tool{position:relative;width:100%;height:50px;line-height:30px;padding:10px 15px;border-width:0 0 1px}.layui-table-page{position:relative;width:100%;padding:7px 7px 0;border-width:1px 0 0;height:41px;font-size:12px}.layui-table-page>div{height:26px}.layui-table-page .layui-laypage{margin:0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span{height:26px;line-height:26px;margin-bottom:10px;border:none;background:0 0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span.layui-laypage-curr{padding:0 12px}.layui-table-page .layui-laypage span{margin-left:0;padding:0}.layui-table-page .layui-laypage .layui-laypage-prev{margin-left:-7px!important}.layui-table-page .layui-laypage .layui-laypage-curr .layui-laypage-em{left:0;top:0;padding:0}.layui-table-page .layui-laypage button,.layui-table-page .layui-laypage input{height:26px;line-height:26px}.layui-table-page .layui-laypage input{width:40px}.layui-table-page .layui-laypage button{padding:0 10px}.layui-table-page select{height:18px}.layui-table-view select[lay-ignore]{display:inline-block}.layui-table-patch .layui-table-cell{padding:0;width:30px}.layui-table-edit{position:absolute;left:0;top:0;width:100%;height:100%;padding:0 14px 1px;border-radius:0;box-shadow:1px 1px 20px rgba(0,0,0,.15)}.layui-table-edit:focus{border-color:#5FB878!important}select.layui-table-edit{padding:0 0 0 10px;border-color:#C9C9C9}.layui-table-view .layui-form-checkbox,.layui-table-view .layui-form-radio,.layui-table-view .layui-form-switch{top:0;margin:0;box-sizing:content-box}.layui-table-view .layui-form-checkbox{top:-1px;height:26px;line-height:26px}body .layui-table-tips .layui-layer-content{background:0 0;padding:0;box-shadow:0 1px 6px rgba(0,0,0,.1)}.layui-table-tips-main{margin:-44px 0 0 -1px;max-height:150px;padding:8px 15px;font-size:14px;overflow-y:scroll;background-color:#fff;color:#333}.layui-table-tips-c{position:absolute;right:-3px;top:-12px;width:18px;height:18px;padding:3px;text-align:center;font-weight:700;border-radius:100%;font-size:14px;cursor:pointer;background-color:#666}.layui-table-tips-c:hover{background-color:#999}.layui-upload-file{display:none!important;opacity:.01;filter:Alpha(opacity=1)}.layui-upload-drag,.layui-upload-form,.layui-upload-wrap{display:inline-block}.layui-upload-list{margin:10px 0}.layui-upload-choose{padding:0 10px;color:#999}.layui-upload-drag{position:relative;padding:30px;border:1px dashed #e2e2e2;background-color:#fff;text-align:center;cursor:pointer;color:#999}.layui-upload-drag .layui-icon{font-size:50px;color:#009688}.layui-upload-drag[lay-over]{border-color:#009688}.layui-upload-iframe{position:absolute;width:0;height:0;border:0;visibility:hidden}.layui-upload-wrap{position:relative;vertical-align:middle}.layui-upload-wrap .layui-upload-file{display:block!important;position:absolute;left:0;top:0;z-index:10;font-size:100px;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-rate,.layui-rate *{display:inline-block;vertical-align:middle}.layui-rate{padding:10px 5px 10px 0;font-size:0}.layui-rate li i.layui-icon{font-size:20px;color:#FFB800;margin-right:5px;transition:all .3s;-webkit-transition:all .3s}.layui-rate li i:hover{cursor:pointer;transform:scale(1.12);-webkit-transform:scale(1.12)}.layui-rate[readonly] li i:hover{cursor:default;transform:scale(1)}.layui-code{position:relative;margin:10px 0;padding:15px;line-height:20px;border:1px solid #ddd;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New;font-size:12px}.layui-tree{line-height:26px}.layui-tree li{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-tree li .layui-tree-spread,.layui-tree li a{display:inline-block;vertical-align:top;height:26px;*display:inline;*zoom:1;cursor:pointer}.layui-tree li a{font-size:0}.layui-tree li a i{font-size:16px}.layui-tree li a cite{padding:0 6px;font-size:14px;font-style:normal}.layui-tree li i{padding-left:6px;color:#333;-moz-user-select:none}.layui-tree li .layui-tree-check{font-size:13px}.layui-tree li .layui-tree-check:hover{color:#009E94}.layui-tree li ul{display:none;margin-left:20px}.layui-tree li .layui-tree-enter{line-height:24px;border:1px dotted #000}.layui-tree-drag{display:none;position:absolute;left:-666px;top:-666px;background-color:#f2f2f2;padding:5px 10px;border:1px dotted #000;white-space:nowrap}.layui-tree-drag i{padding-right:5px}.layui-nav{position:relative;padding:0 20px;background-color:#393D49;color:#fff;border-radius:2px;font-size:0;box-sizing:border-box}.layui-nav *{font-size:14px}.layui-nav .layui-nav-item{position:relative;display:inline-block;*display:inline;*zoom:1;vertical-align:middle;line-height:60px}.layui-nav .layui-nav-item a{display:block;padding:0 20px;color:#fff;color:rgba(255,255,255,.7);transition:all .3s;-webkit-transition:all .3s}.layui-nav .layui-this:after,.layui-nav-bar,.layui-nav-tree .layui-nav-itemed:after{position:absolute;left:0;top:0;width:0;height:5px;background-color:#5FB878;transition:all .2s;-webkit-transition:all .2s}.layui-nav-bar{z-index:1000}.layui-nav .layui-nav-item a:hover,.layui-nav .layui-this a{color:#fff}.layui-nav .layui-this:after{content:'';top:auto;bottom:0;width:100%}.layui-nav-img{width:30px;height:30px;margin-right:10px;border-radius:50%}.layui-nav .layui-nav-more{content:'';width:0;height:0;border-style:solid dashed dashed;border-color:#fff transparent transparent;overflow:hidden;cursor:pointer;transition:all .2s;-webkit-transition:all .2s;position:absolute;top:50%;right:3px;margin-top:-3px;border-width:6px;border-top-color:rgba(255,255,255,.7)}.layui-nav .layui-nav-mored,.layui-nav-itemed>a .layui-nav-more{margin-top:-9px;border-style:dashed dashed solid;border-color:transparent transparent #fff}.layui-nav-child{display:none;position:absolute;left:0;top:65px;min-width:100%;line-height:36px;padding:5px 0;box-shadow:0 2px 4px rgba(0,0,0,.12);border:1px solid #d2d2d2;background-color:#fff;z-index:100;border-radius:2px;white-space:nowrap}.layui-nav .layui-nav-child a{color:#333}.layui-nav .layui-nav-child a:hover{background-color:#f2f2f2;color:#000}.layui-nav-child dd{position:relative}.layui-nav .layui-nav-child dd.layui-this a,.layui-nav-child dd.layui-this{background-color:#5FB878;color:#fff}.layui-nav-child dd.layui-this:after{display:none}.layui-nav-tree{width:200px;padding:0}.layui-nav-tree .layui-nav-item{display:block;width:100%;line-height:45px}.layui-nav-tree .layui-nav-item a{position:relative;height:45px;line-height:45px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-nav-tree .layui-nav-item a:hover{background-color:#4E5465}.layui-nav-tree .layui-nav-bar{width:5px;height:0;background-color:#009688}.layui-nav-tree .layui-nav-child dd.layui-this,.layui-nav-tree .layui-nav-child dd.layui-this a,.layui-nav-tree .layui-this,.layui-nav-tree .layui-this>a,.layui-nav-tree .layui-this>a:hover{background-color:#009688;color:#fff}.layui-nav-tree .layui-this:after{display:none}.layui-nav-itemed>a,.layui-nav-tree .layui-nav-title a,.layui-nav-tree .layui-nav-title a:hover{color:#fff!important}.layui-nav-tree .layui-nav-child{position:relative;z-index:0;top:0;border:none;box-shadow:none}.layui-nav-tree .layui-nav-child a{height:40px;line-height:40px;color:#fff;color:rgba(255,255,255,.7)}.layui-nav-tree .layui-nav-child,.layui-nav-tree .layui-nav-child a:hover{background:0 0;color:#fff}.layui-nav-tree .layui-nav-more{right:10px}.layui-nav-itemed>.layui-nav-child{display:block;padding:0;background-color:rgba(0,0,0,.3)!important}.layui-nav-itemed>.layui-nav-child>.layui-this>.layui-nav-child{display:block}.layui-nav-side{position:fixed;top:0;bottom:0;left:0;overflow-x:hidden;z-index:999}.layui-bg-blue .layui-nav-bar,.layui-bg-blue .layui-nav-itemed:after,.layui-bg-blue .layui-this:after{background-color:#93D1FF}.layui-bg-blue .layui-nav-child dd.layui-this{background-color:#1E9FFF}.layui-bg-blue .layui-nav-itemed>a,.layui-nav-tree.layui-bg-blue .layui-nav-title a,.layui-nav-tree.layui-bg-blue .layui-nav-title a:hover{background-color:#007DDB!important}.layui-breadcrumb{visibility:hidden;font-size:0}.layui-breadcrumb>*{font-size:14px}.layui-breadcrumb a{color:#999!important}.layui-breadcrumb a:hover{color:#5FB878!important}.layui-breadcrumb a cite{color:#666;font-style:normal}.layui-breadcrumb span[lay-separator]{margin:0 10px;color:#999}.layui-tab{margin:10px 0;text-align:left!important}.layui-tab[overflow]>.layui-tab-title{overflow:hidden}.layui-tab-title{position:relative;left:0;height:40px;white-space:nowrap;font-size:0;border-bottom-width:1px;border-bottom-style:solid;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;font-size:14px;transition:all .2s;-webkit-transition:all .2s;position:relative;line-height:40px;min-width:65px;padding:0 15px;text-align:center;cursor:pointer}.layui-tab-title li a{display:block}.layui-tab-title .layui-this{color:#000}.layui-tab-title .layui-this:after{position:absolute;left:0;top:0;content:'';width:100%;height:41px;border-width:1px;border-style:solid;border-bottom-color:#fff;border-radius:2px 2px 0 0;box-sizing:border-box;pointer-events:none}.layui-tab-bar{position:absolute;right:0;top:0;z-index:10;width:30px;height:39px;line-height:39px;border-width:1px;border-style:solid;border-radius:2px;text-align:center;background-color:#fff;cursor:pointer}.layui-tab-bar .layui-icon{position:relative;display:inline-block;top:3px;transition:all .3s;-webkit-transition:all .3s}.layui-tab-item{display:none}.layui-tab-more{padding-right:30px;height:auto!important;white-space:normal!important}.layui-tab-more li.layui-this:after{border-bottom-color:#e2e2e2;border-radius:2px}.layui-tab-more .layui-tab-bar .layui-icon{top:-2px;top:3px\9;-webkit-transform:rotate(180deg);transform:rotate(180deg)}:root .layui-tab-more .layui-tab-bar .layui-icon{top:-2px\0/IE9}.layui-tab-content{padding:10px}.layui-tab-title li .layui-tab-close{position:relative;display:inline-block;width:18px;height:18px;line-height:20px;margin-left:8px;top:1px;text-align:center;font-size:14px;color:#c2c2c2;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li .layui-tab-close:hover{border-radius:2px;background-color:#FF5722;color:#fff}.layui-tab-brief>.layui-tab-title .layui-this{color:#009688}.layui-tab-brief>.layui-tab-more li.layui-this:after,.layui-tab-brief>.layui-tab-title .layui-this:after{border:none;border-radius:0;border-bottom:2px solid #5FB878}.layui-tab-brief[overflow]>.layui-tab-title .layui-this:after{top:-1px}.layui-tab-card{border-width:1px;border-style:solid;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.1)}.layui-tab-card>.layui-tab-title{background-color:#f2f2f2}.layui-tab-card>.layui-tab-title li{margin-right:-1px;margin-left:-1px}.layui-tab-card>.layui-tab-title .layui-this{background-color:#fff}.layui-tab-card>.layui-tab-title .layui-this:after{border-top:none;border-width:1px;border-bottom-color:#fff}.layui-tab-card>.layui-tab-title .layui-tab-bar{height:40px;line-height:40px;border-radius:0;border-top:none;border-right:none}.layui-tab-card>.layui-tab-more .layui-this{background:0 0;color:#5FB878}.layui-tab-card>.layui-tab-more .layui-this:after{border:none}.layui-timeline{padding-left:5px}.layui-timeline-item{position:relative;padding-bottom:20px}.layui-timeline-axis{position:absolute;left:-5px;top:0;z-index:10;width:20px;height:20px;line-height:20px;background-color:#fff;color:#5FB878;border-radius:50%;text-align:center;cursor:pointer}.layui-timeline-axis:hover{color:#FF5722}.layui-timeline-item:before{content:'';position:absolute;left:5px;top:0;z-index:0;width:1px;height:100%}.layui-timeline-item:last-child:before{display:none}.layui-timeline-item:first-child:before{display:block}.layui-timeline-content{padding-left:25px}.layui-timeline-title{position:relative;margin-bottom:10px}.layui-badge,.layui-badge-dot,.layui-badge-rim{position:relative;display:inline-block;padding:0 6px;font-size:12px;text-align:center;background-color:#FF5722;color:#fff;border-radius:2px}.layui-badge{height:18px;line-height:18px}.layui-badge-dot{width:8px;height:8px;padding:0;border-radius:50%}.layui-badge-rim{height:18px;line-height:18px;border-width:1px;border-style:solid;background-color:#fff;color:#666}.layui-btn .layui-badge,.layui-btn .layui-badge-dot{margin-left:5px}.layui-nav .layui-badge,.layui-nav .layui-badge-dot{position:absolute;top:50%;margin:-8px 6px 0}.layui-tab-title .layui-badge,.layui-tab-title .layui-badge-dot{left:5px;top:-2px}.layui-carousel{position:relative;left:0;top:0;background-color:#f8f8f8}.layui-carousel>[carousel-item]{position:relative;width:100%;height:100%;overflow:hidden}.layui-carousel>[carousel-item]:before{position:absolute;content:'\e63d';left:50%;top:50%;width:100px;line-height:20px;margin:-10px 0 0 -50px;text-align:center;color:#c2c2c2;font-family:layui-icon!important;font-size:30px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-carousel>[carousel-item]>*{display:none;position:absolute;left:0;top:0;width:100%;height:100%;background-color:#f8f8f8;transition-duration:.3s;-webkit-transition-duration:.3s}.layui-carousel-updown>*{-webkit-transition:.3s ease-in-out up;transition:.3s ease-in-out up}.layui-carousel-arrow{display:none\9;opacity:0;position:absolute;left:10px;top:50%;margin-top:-18px;width:36px;height:36px;line-height:36px;text-align:center;font-size:20px;border:0;border-radius:50%;background-color:rgba(0,0,0,.2);color:#fff;-webkit-transition-duration:.3s;transition-duration:.3s;cursor:pointer}.layui-carousel-arrow[lay-type=add]{left:auto!important;right:10px}.layui-carousel:hover .layui-carousel-arrow[lay-type=add],.layui-carousel[lay-arrow=always] .layui-carousel-arrow[lay-type=add]{right:20px}.layui-carousel[lay-arrow=always] .layui-carousel-arrow{opacity:1;left:20px}.layui-carousel[lay-arrow=none] .layui-carousel-arrow{display:none}.layui-carousel-arrow:hover,.layui-carousel-ind ul:hover{background-color:rgba(0,0,0,.35)}.layui-carousel:hover .layui-carousel-arrow{display:block\9;opacity:1;left:20px}.layui-carousel-ind{position:relative;top:-35px;width:100%;line-height:0!important;text-align:center;font-size:0}.layui-carousel[lay-indicator=outside]{margin-bottom:30px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind{top:10px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind ul{background-color:rgba(0,0,0,.5)}.layui-carousel[lay-indicator=none] .layui-carousel-ind{display:none}.layui-carousel-ind ul{display:inline-block;padding:5px;background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li{display:inline-block;width:10px;height:10px;margin:0 3px;font-size:14px;background-color:#e2e2e2;background-color:rgba(255,255,255,.5);border-radius:50%;cursor:pointer;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li:hover{background-color:rgba(255,255,255,.7)}.layui-carousel-ind li.layui-this{background-color:#fff}.layui-carousel>[carousel-item]>.layui-carousel-next,.layui-carousel>[carousel-item]>.layui-carousel-prev,.layui-carousel>[carousel-item]>.layui-this{display:block}.layui-carousel>[carousel-item]>.layui-this{left:0}.layui-carousel>[carousel-item]>.layui-carousel-prev{left:-100%}.layui-carousel>[carousel-item]>.layui-carousel-next{left:100%}.layui-carousel>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel>[carousel-item]>.layui-carousel-prev.layui-carousel-right{left:0}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-left{left:-100%}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-right{left:100%}.layui-carousel[lay-anim=updown] .layui-carousel-arrow{left:50%!important;top:20px;margin:0 0 0 -18px}.layui-carousel[lay-anim=updown]>[carousel-item]>*,.layui-carousel[lay-anim=fade]>[carousel-item]>*{left:0!important}.layui-carousel[lay-anim=updown] .layui-carousel-arrow[lay-type=add]{top:auto!important;bottom:20px}.layui-carousel[lay-anim=updown] .layui-carousel-ind{position:absolute;top:50%;right:20px;width:auto;height:auto}.layui-carousel[lay-anim=updown] .layui-carousel-ind ul{padding:3px 5px}.layui-carousel[lay-anim=updown] .layui-carousel-ind li{display:block;margin:6px 0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next{top:100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-left{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-right{top:100%}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev{opacity:0}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{opacity:1}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-right{opacity:0}.layui-fixbar{position:fixed;right:15px;bottom:15px;z-index:9999}.layui-fixbar li{width:50px;height:50px;line-height:50px;margin-bottom:1px;text-align:center;cursor:pointer;font-size:30px;background-color:#9F9F9F;color:#fff;border-radius:2px;opacity:.95}.layui-fixbar li:hover{opacity:.85}.layui-fixbar li:active{opacity:1}.layui-fixbar .layui-fixbar-top{display:none;font-size:40px}body .layui-util-face{border:none;background:0 0}body .layui-util-face .layui-layer-content{padding:0;background-color:#fff;color:#666;box-shadow:none}.layui-util-face .layui-layer-TipsG{display:none}.layui-util-face ul{position:relative;width:372px;padding:10px;border:1px solid #D9D9D9;background-color:#fff;box-shadow:0 0 20px rgba(0,0,0,.2)}.layui-util-face ul li{cursor:pointer;float:left;border:1px solid #e8e8e8;height:22px;width:26px;overflow:hidden;margin:-1px 0 0 -1px;padding:4px 2px;text-align:center}.layui-util-face ul li:hover{position:relative;z-index:2;border:1px solid #eb7350;background:#fff9ec}.layui-anim{-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-anim.layui-icon{display:inline-block}.layui-anim-loop{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.layui-trans,.layui-trans a{transition:all .3s;-webkit-transition:all .3s}@-webkit-keyframes layui-rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@keyframes layui-rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}}.layui-anim-rotate{-webkit-animation-name:layui-rotate;animation-name:layui-rotate;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:linear;animation-timing-function:linear}@-webkit-keyframes layui-up{from{-webkit-transform:translate3d(0,100%,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-up{from{transform:translate3d(0,100%,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-up{-webkit-animation-name:layui-up;animation-name:layui-up}@-webkit-keyframes layui-upbit{from{-webkit-transform:translate3d(0,30px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-upbit{from{transform:translate3d(0,30px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-upbit{-webkit-animation-name:layui-upbit;animation-name:layui-upbit}@-webkit-keyframes layui-scale{0%{opacity:.3;-webkit-transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale{0%{opacity:.3;-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-ms-transform:scale(1);transform:scale(1)}}.layui-anim-scale{-webkit-animation-name:layui-scale;animation-name:layui-scale}@-webkit-keyframes layui-scale-spring{0%{opacity:.5;-webkit-transform:scale(.5)}80%{opacity:.8;-webkit-transform:scale(1.1)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale-spring{0%{opacity:.5;transform:scale(.5)}80%{opacity:.8;transform:scale(1.1)}100%{opacity:1;transform:scale(1)}}.layui-anim-scaleSpring{-webkit-animation-name:layui-scale-spring;animation-name:layui-scale-spring}@-webkit-keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}@keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}.layui-anim-fadein{-webkit-animation-name:layui-fadein;animation-name:layui-fadein}@-webkit-keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}.layui-anim-fadeout{-webkit-animation-name:layui-fadeout;animation-name:layui-fadeout} \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/css/layui.mobile.css b/agent-analyer/src/main/resources/static/js/layui/css/layui.mobile.css new file mode 100644 index 0000000..4e5ea08 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/css/layui.mobile.css @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,legend,li,ol,p,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}html{font:12px 'Helvetica Neue','PingFang SC',STHeitiSC-Light,Helvetica,Arial,sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a,button,input{-webkit-tap-highlight-color:rgba(255,0,0,0)}a{text-decoration:none;background:0 0}a:active,a:hover{outline:0}table{border-collapse:collapse;border-spacing:0}li{list-style:none}b,strong{font-weight:700}h1,h2,h3,h4,h5,h6{font-weight:500}address,cite,dfn,em,var{font-style:normal}dfn{font-style:italic}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}img{border:0;vertical-align:bottom}.layui-inline,input,label{vertical-align:middle}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0;outline:0}button,select{text-transform:none}select{-webkit-appearance:none;border:none}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=1.0.7);src:url(../font/iconfont.eot?v=1.0.7#iefix) format('embedded-opentype'),url(../font/iconfont.woff?v=1.0.7) format('woff'),url(../font/iconfont.ttf?v=1.0.7) format('truetype'),url(../font/iconfont.svg?v=1.0.7#iconfont) format('svg')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-box,.layui-box *{-webkit-box-sizing:content-box!important;-moz-box-sizing:content-box!important;box-sizing:content-box!important}.layui-border-box,.layui-border-box *{-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-inline{position:relative;display:inline-block;*display:inline;*zoom:1}.layui-edge,.layui-upload-iframe{position:absolute;width:0;height:0}.layui-edge{border-style:dashed;border-color:transparent;overflow:hidden}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-unselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-disabled,.layui-disabled:active{background-color:#d2d2d2!important;color:#fff!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}.layui-upload-iframe{border:0;visibility:hidden}.layui-upload-enter{border:1px solid #009E94;background-color:#009E94;color:#fff;-webkit-transform:scale(1.1);transform:scale(1.1)}@-webkit-keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.layui-m-anim-scale{animation-name:layui-m-anim-scale;-webkit-animation-name:layui-m-anim-scale}@-webkit-keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.layui-m-anim-up{-webkit-animation-name:layui-m-anim-up;animation-name:layui-m-anim-up}@-webkit-keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-left{-webkit-animation-name:layui-m-anim-left;animation-name:layui-m-anim-left}@-webkit-keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-right{-webkit-animation-name:layui-m-anim-right;animation-name:layui-m-anim-right}@-webkit-keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}@keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}.layui-m-anim-lout{-webkit-animation-name:layui-m-anim-lout;animation-name:layui-m-anim-lout}@-webkit-keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}@keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}.layui-m-anim-rout{-webkit-animation-name:layui-m-anim-rout;animation-name:layui-m-anim-rout}.layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.layui-m-layermain,.layui-m-layershade{position:fixed;left:0;top:0;width:100%;height:100%}.layui-m-layershade{background-color:rgba(0,0,0,.7);pointer-events:auto}.layui-m-layermain{display:table;font-family:Helvetica,arial,sans-serif;pointer-events:none}.layui-m-layermain .layui-m-layersection{display:table-cell;vertical-align:middle;text-align:center}.layui-m-layerchild{position:relative;display:inline-block;text-align:left;background-color:#fff;font-size:14px;border-radius:5px;box-shadow:0 0 8px rgba(0,0,0,.1);pointer-events:auto;-webkit-overflow-scrolling:touch;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-m-layer0 .layui-m-layerchild{width:90%;max-width:640px}.layui-m-layer1 .layui-m-layerchild{border:none;border-radius:0}.layui-m-layer2 .layui-m-layerchild{width:auto;max-width:260px;min-width:40px;border:none;background:0 0;box-shadow:none;color:#fff}.layui-m-layerchild h3{padding:0 10px;height:60px;line-height:60px;font-size:16px;font-weight:400;border-radius:5px 5px 0 0;text-align:center}.layui-m-layerbtn span,.layui-m-layerchild h3{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-m-layercont{padding:50px 30px;line-height:22px;text-align:center}.layui-m-layer1 .layui-m-layercont{padding:0;text-align:left}.layui-m-layer2 .layui-m-layercont{text-align:center;padding:0;line-height:0}.layui-m-layer2 .layui-m-layercont i{width:25px;height:25px;margin-left:8px;display:inline-block;background-color:#fff;border-radius:100%;-webkit-animation:layui-m-anim-loading 1.4s infinite ease-in-out;animation:layui-m-anim-loading 1.4s infinite ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-m-layerbtn,.layui-m-layerbtn span{position:relative;text-align:center;border-radius:0 0 5px 5px}.layui-m-layer2 .layui-m-layercont p{margin-top:20px}@-webkit-keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}.layui-m-layer2 .layui-m-layercont i:first-child{margin-left:0;-webkit-animation-delay:-.32s;animation-delay:-.32s}.layui-m-layer2 .layui-m-layercont i.layui-m-layerload{-webkit-animation-delay:-.16s;animation-delay:-.16s}.layui-m-layer2 .layui-m-layercont>div{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px} \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/css/modules/code.css b/agent-analyer/src/main/resources/static/js/layui/css/modules/code.css new file mode 100644 index 0000000..aaade1d --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/css/modules/code.css @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none} \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/css/modules/laydate/default/laydate.css b/agent-analyer/src/main/resources/static/js/layui/css/modules/laydate/default/laydate.css new file mode 100644 index 0000000..6e1de7d --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/css/modules/laydate/default/laydate.css @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + .laydate-set-ym,.layui-laydate,.layui-laydate *,.layui-laydate-list{box-sizing:border-box}html #layuicss-laydate{display:none;position:absolute;width:1989px}.layui-laydate *{margin:0;padding:0}.layui-laydate{position:absolute;z-index:66666666;margin:5px 0;border-radius:2px;font-size:14px;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:laydate-upbit;animation-name:laydate-upbit}.layui-laydate-main{width:272px}.layui-laydate-content td,.layui-laydate-header *,.layui-laydate-list li{transition-duration:.3s;-webkit-transition-duration:.3s}@-webkit-keyframes laydate-upbit{from{-webkit-transform:translate3d(0,20px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes laydate-upbit{from{transform:translate3d(0,20px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-laydate-static{position:relative;z-index:0;display:inline-block;margin:0;-webkit-animation:none;animation:none}.laydate-ym-show .laydate-next-m,.laydate-ym-show .laydate-prev-m{display:none!important}.laydate-ym-show .laydate-next-y,.laydate-ym-show .laydate-prev-y{display:inline-block!important}.laydate-time-show .laydate-set-ym span[lay-type=month],.laydate-time-show .laydate-set-ym span[lay-type=year],.laydate-time-show .layui-laydate-header .layui-icon,.laydate-ym-show .laydate-set-ym span[lay-type=month]{display:none!important}.layui-laydate-header{position:relative;line-height:30px;padding:10px 70px 5px}.laydate-set-ym span,.layui-laydate-header i{padding:0 5px;cursor:pointer}.layui-laydate-header *{display:inline-block;vertical-align:bottom}.layui-laydate-header i{position:absolute;top:10px;color:#999;font-size:18px}.layui-laydate-header i.laydate-prev-y{left:15px}.layui-laydate-header i.laydate-prev-m{left:45px}.layui-laydate-header i.laydate-next-y{right:15px}.layui-laydate-header i.laydate-next-m{right:45px}.laydate-set-ym{width:100%;text-align:center;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.laydate-time-text{cursor:default!important}.layui-laydate-content{position:relative;padding:10px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-laydate-content table{border-collapse:collapse;border-spacing:0}.layui-laydate-content td,.layui-laydate-content th{width:36px;height:30px;padding:5px;text-align:center}.layui-laydate-content td{position:relative;cursor:pointer}.laydate-day-mark{position:absolute;left:0;top:0;width:100%;height:100%;line-height:30px;font-size:12px;overflow:hidden}.laydate-day-mark::after{position:absolute;content:'';right:2px;top:2px;width:5px;height:5px;border-radius:50%}.layui-laydate-footer{position:relative;height:46px;line-height:26px;padding:10px 20px}.layui-laydate-footer span{margin-right:15px;display:inline-block;cursor:pointer;font-size:12px}.layui-laydate-footer span:hover{color:#5FB878}.laydate-footer-btns{position:absolute;right:10px;top:10px}.laydate-footer-btns span{height:26px;line-height:26px;margin:0 0 0 -1px;padding:0 10px;border:1px solid #C9C9C9;background-color:#fff;white-space:nowrap;vertical-align:top;border-radius:2px}.layui-laydate-list>li,.layui-laydate-range .layui-laydate-main{display:inline-block;vertical-align:middle}.layui-laydate-list{position:absolute;left:0;top:0;width:100%;height:100%;padding:10px;background-color:#fff}.layui-laydate-list>li{position:relative;width:33.3%;height:36px;line-height:36px;margin:3px 0;text-align:center;cursor:pointer}.laydate-month-list>li{width:25%;margin:17px 0}.laydate-time-list>li{height:100%;margin:0;line-height:normal;cursor:default}.laydate-time-list p{position:relative;top:-4px;line-height:29px}.laydate-time-list ol{height:181px;overflow:hidden}.laydate-time-list>li:hover ol{overflow-y:auto}.laydate-time-list ol li{width:130%;padding-left:33px;line-height:30px;text-align:left;cursor:pointer}.layui-laydate-hint{position:absolute;top:115px;left:50%;width:250px;margin-left:-125px;line-height:20px;padding:15px;text-align:center;font-size:12px}.layui-laydate-range{width:546px}.layui-laydate-range .laydate-main-list-0 .laydate-next-m,.layui-laydate-range .laydate-main-list-0 .laydate-next-y,.layui-laydate-range .laydate-main-list-1 .laydate-prev-m,.layui-laydate-range .laydate-main-list-1 .laydate-prev-y{display:none}.layui-laydate-range .laydate-main-list-1 .layui-laydate-content{border-left:1px solid #e2e2e2}.layui-laydate,.layui-laydate-hint{border:1px solid #d2d2d2;box-shadow:0 2px 4px rgba(0,0,0,.12);background-color:#fff;color:#666}.layui-laydate-header{border-bottom:1px solid #e2e2e2}.layui-laydate-header i:hover,.layui-laydate-header span:hover{color:#5FB878}.layui-laydate-content{border-top:none 0;border-bottom:none 0}.layui-laydate-content th{font-weight:400;color:#333}.layui-laydate-content td{color:#666}.layui-laydate-content td.laydate-selected{background-color:#00F7DE}.laydate-selected:hover{background-color:#00F7DE!important}.layui-laydate-content td:hover,.layui-laydate-list li:hover{background-color:#eaeaea;color:#333}.laydate-time-list li ol{margin:0;padding:0;border:1px solid #e2e2e2;border-left-width:0}.laydate-time-list li:first-child ol{border-left-width:1px}.laydate-time-list>li:hover{background:0 0}.layui-laydate-content .laydate-day-next,.layui-laydate-content .laydate-day-prev{color:#d2d2d2}.laydate-selected.laydate-day-next,.laydate-selected.laydate-day-prev{background-color:#f8f8f8!important}.layui-laydate-footer{border-top:1px solid #e2e2e2}.layui-laydate-hint{color:#FF5722}.laydate-day-mark::after{background-color:#5FB878}.layui-laydate-content td.layui-this .laydate-day-mark::after{display:none}.layui-laydate-footer span[lay-type=date]{color:#5FB878}.layui-laydate .layui-this{background-color:#009688!important;color:#fff!important}.layui-laydate .laydate-disabled,.layui-laydate .laydate-disabled:hover{background:0 0!important;color:#d2d2d2!important;cursor:not-allowed!important;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.laydate-theme-molv{border:none}.laydate-theme-molv.layui-laydate-range{width:548px}.laydate-theme-molv .layui-laydate-main{width:274px}.laydate-theme-molv .layui-laydate-header{border:none;background-color:#009688}.laydate-theme-molv .layui-laydate-header i,.laydate-theme-molv .layui-laydate-header span{color:#f6f6f6}.laydate-theme-molv .layui-laydate-header i:hover,.laydate-theme-molv .layui-laydate-header span:hover{color:#fff}.laydate-theme-molv .layui-laydate-content{border:1px solid #e2e2e2;border-top:none;border-bottom:none}.laydate-theme-molv .laydate-main-list-1 .layui-laydate-content{border-left:none}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li,.laydate-theme-grid .layui-laydate-content td,.laydate-theme-grid .layui-laydate-content thead,.laydate-theme-molv .layui-laydate-footer{border:1px solid #e2e2e2}.laydate-theme-grid .laydate-selected,.laydate-theme-grid .laydate-selected:hover{background-color:#f2f2f2!important;color:#009688!important}.laydate-theme-grid .laydate-selected.laydate-day-next,.laydate-theme-grid .laydate-selected.laydate-day-prev{color:#d2d2d2!important}.laydate-theme-grid .laydate-month-list,.laydate-theme-grid .laydate-year-list{margin:1px 0 0 1px}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li{margin:0 -1px -1px 0}.laydate-theme-grid .laydate-year-list>li{height:43px;line-height:43px}.laydate-theme-grid .laydate-month-list>li{height:71px;line-height:71px} \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/icon-ext.png b/agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/icon-ext.png new file mode 100644 index 0000000000000000000000000000000000000000..bbbb669bb311514baa5db3a6a00b4644d0e280f1 GIT binary patch literal 5911 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 bWW^%>x7XlHV^#Q1uOX^B?ki3(VEg|7!QHuF literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/loading-1.gif b/agent-analyer/src/main/resources/static/js/layui/css/modules/layer/default/loading-1.gif new file mode 100644 index 0000000000000000000000000000000000000000..db3a483e4b74971fbfb1cc0fb6499852cedfe650 GIT binary patch literal 701 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 Di}J5T literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/font/iconfont.svg b/agent-analyer/src/main/resources/static/js/layui/font/iconfont.svg new file mode 100644 index 0000000..e1c15af --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/font/iconfont.svg @@ -0,0 +1,468 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/agent-analyer/src/main/resources/static/js/layui/font/iconfont.ttf b/agent-analyer/src/main/resources/static/js/layui/font/iconfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..43a316134dbeef2018f808fa14393ffc75967078 GIT binary patch literal 39968 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 Yx#{Q<=3XG#!lMCcvm4L#YDLBW2eTlXG5`Po literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/font/iconfont.woff b/agent-analyer/src/main/resources/static/js/layui/font/iconfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..7fd98e35fcb768eaa3e8e9ffda1a888cf225a5ce GIT binary patch literal 26328 zcmV)xK$E|BPew)n0RR910A|<#3jhEB0GuEI0RR91000000000000000000000000- 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/0.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/0.gif new file mode 100644 index 0000000000000000000000000000000000000000..a63f0d523561c523476e5330a0d67eb7318cb187 GIT binary patch literal 2689 zcmbVNX;c%*77h|fSVf7btRc9;2uTMBA%GGHLDVpaID<1f5|U^lkc1>iKxY6!1rbG@ zd2aAjaK>G62Sr5@Tv%lj5ZMBV%8n>ts{1vL_@3u@=Ev*PeX8o#z2E)rQq{hGY`QDg z1Y?5vfMDRklNoDFn+y1$-V+&nI?*1FjNVaKeTNKPP*>Wj%Sp)S#fb{Le6b$#p-{ol zsW=k|kKKQjucL0^f#x9i&7RS^Kv2J=HOHjvYO1<34zzKv#bH4$O z#ZFq03?oqI0iLr!jWcLn2O9kz?9&3Z4A8^@wQiuy9MsUj%i!U1H=q*1{h6R?C3x4%9Z~bY?lcTmR7j#Mw>w(q{eMOd|)qc5L4N&c* zuC#-D;)kkvpmjZHTn0Kef@(+gGxF;@#-Mq1Z|Ta0Q`*Srt;^f+@S9XnW&vKOd~B6~ zHyNOJFDSJDP5y6-g21a^k$3r^XIIl%Q@Cp@Xb*$?kBqma0hL(Ye-`v4To=x`Eyook z8;-r))qmH#x4;x0I$fJ%qOMq2{D&DbUW-iBk2QvUX!>cQSp;gFz)RlCiz^#1xfLC- z87Zj=eCi$V&2U#t-?L}$cK{g8qbIOabq>? zp4*0{8D2G6Ot>!#v2jzcD7SojpVX4O3Vsy}_iV4&YYK*PK%Fl#a&`FG zM$objsAAv_jp9{L8DlbVvx3c0C zr|Pl<9y$m2ro$@nL4nz&9SmgnEc|9WGSQyB1p-}ha92Fse?ZQ_Bctc8B-z2L2xQ`g z`dyy7!B5?H1nxhi9>_)z#2V4__X%3<%?$9NI#4tL15Xk`R&u3e$bmwRh?tD)leSzDDEKdpZ)(efVx$6cpg=B(lJNwny3Ld^ zn!A5?G^vQPVSFzWp+(__vn0Hz2!UAP$MPVdI}UsipFwe?qgixuqPS2gG&%&)Szcb= zUTn5A#oNb)?!ubF_?9i(g+_B>QeAz}{6mlrgqnM?ATOpDlj_W-In$Ej}3 z2Pdarnjc+!IX(h0dS+zk;m|-}p#C^9KK9}L=(~~Op}~RvzTUTQUiZA}?&?%^w70dk zylj5a)Y$O6zOJ^W`dL+FMR{52(=Vb2{W~l3=#j&R4*u(p-w$N$Pv5sUZBOd%l;mAIcO)flPl%8E z?bmHvV`G%jTcQ+lS)^1VjtCFiED~ zT(R75nJ?Rih87J7rZv4a|qPj4f%`NKP{w9zJutaJ^PRj7vDCX2@s-1fMsBq7R3UP)M+%4tA?D+}XvHFzz*y%L=kmCmK7^vJPkuUQkAmPS3^L3MDk2U`FtXG(OBw%kf#qS1^68svpOwv@mo8;P zDGCaXYN>-Qp0~Xx+rV_GI`^sF6U7K8(~zl=;$%)R)~gj@bP&VoMw_I4V?6f(!^$+;TlKZKNWRvr5bBw-5Huyw**;mBJ z$e)Q{?zFlcJ6(9*C`9kUZrt2~XFpTKnRkA%Zm|z`^-ujm+}lE&XVKZs-c=Znar+N+ CBC!|% literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/1.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/1.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2b78b218996f70773540d0d3a8649d3c2d199a2 GIT binary patch literal 5514 zcmeI0X;f2Z8pm%|0tv}wF|q`>VUY-kh?astAQA;dB?6_fabYVUO9#+)7)Q(s2}KDA zighC_TBPE*AR-hSAO%6du#{TxXn-QNiVcVgN|Fij%v8I5oO9-!+)ww*d!7%!|9hVI z{|nx@!P_T>2oQmXY@j$fRCS-Fst#8Fl-Ji<_wt8B#oMn7hHBnZ4SIK-bRDmyJo~XxHIe<@S;}yGsph#%R&3pM=?m3xsA@drb+`R>L5S-6 zz>yk^YAiuB+oYNAZ_LF?#Sxmh?wKCnks9lU+z5q2ar+DGPfeziHw@J?S6jsf&!0cP zdJv^~b+7qw%%v2U$@--gvCCCsdk<_PHte%$Jmk_-lRDCrG;npxi~bPRgV?D7zN#&_ zCe2g*w7m0(@9fi`>xz@w^U1XtK2y!sP1)pEFP~|YwX*|ZH)_kL?|yde{AtyLq;?6r zrS5!RPuG*%o2S}r6<3x__s6NL<5VMzZyPSC`gf@xv3stwub#tO^-JmF&hTmZ_Q|h(Unn2|LQqcJljcz`CqNT-d#=P4TsI+`$f9Ch+HH6_=gXNx+LO~J;=#8M2bsj0pAsmE~jpC z+e9r#gRZAtL!FaYC?7{MaSw`>N^<&&ALWFFS+Qsk`6z^CS-}uaQG8o7OPH^h*N!^J zB?bn*FiR_2UPN3N6Xq3j4@!m%=yWd_((FOaO!^t=l&v`jJMzn0XXJy!`5gR078K1v9z+wE}3v}$MR=6 zIFTWuo=H)j?LKN}v%e&C+c=j~Y*=?dE}h{YLpr1Us*A?L@@)yGj!AMTkYgK|JtLTp zUWYwtNqJJ#_vQJ4FS9uod6LmbIk@-lL+QNel<(bl-msVKb!gj$+4V_uU<(q(x}GL@ zP{By=zzPD^M&24#*nggXldXc+BNE$8YZmMYeZm}wO7Pr>N#jQ-B(?~MM===Y3%&*=Az{%xjzo9W+X z`aYxYGyeyD21WR52Y^Qx#cLG+wFnJr4M0ddVvCPf0r+fTZ3%Hb=^BdjXJOs64!{j- z=8t&W0hH)!X479OfRW#}%z;G(K(o*q2{@ATH=C@;d9$e&vFpb-3t_8bq(m%AT5@Bi zGKi3|JlPKn^yD5RScM;yAK_GS4BiW0*wZ&o;~|Z$kQjO8WbdgJcAJw{tw!Ydd%mLV z?5ZPx4kl~B=EBi-Yvfii`n%!vDN)FMpTt8(Ew_zvl;e_+rl=Jxb0hxKtiGzC91`BW zqTyE4hbwpcrhAe4$e0eaKLebbF8(+pE&#b81IH#>A%oS!(rt_mamlx3=#H zfJ`;IZGvzuAmC!j{5rHu0AsnC&xJqi665{5&Q?zcb2^b9QInYSv`~04FS!aFd?!vT z?KXphV}y6&glQ}sz4LCID8Gpl_h}Ya87wsNA?=`Nl$ZW;|K?|kRPTx)u)uh~1X4b8 z&WAd9zsAXep^cMydzlIptr}~=83}fgrZaeGbNI#uc}5V>o9Fz&=fwToM4rGRP+`G| nqjLpT%jon>&iI|hI5DGFNqSH|Meaf1bVU=!q}mt-2YmkxGX3TC literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/10.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/10.gif new file mode 100644 index 0000000000000000000000000000000000000000..556c7e326801a0ea090bd693ca43807925d3cbc2 GIT binary patch literal 2797 zcmd^A`#)6c8Xsolx+O`H%Q93$nTyL{n9MM)A-684%gM}GWn?bROqpmGJGZ^Fi(PxC zQc0BVBpao@iAv-WQX~e2E`%;_^UgXmoi68xeb(o*-uGGG@ArA0^?g3?^6~R_ zc3~Q#3{evksAPYm+ldxe`B>25hYY5H$5ilyap#a4=-C9i!a--i^?{+Kq>xB__^Jl4Vn&qDieZbVwCxhx2@ zILKOvg+lCu6V~AQ`is#P@}Y#M=jR}wYR+vn|5TiK@RaXU!OgY6vPDVqMt==D71BV~Djr?Wup*KT5)s0Nt z1Ea;`y*Z$Vg^U%+NAu)^dy4m>3*sq2x(&3`O809f`!AG_6@bUi^1&ZV_v?Zdd(dFk zaB1^UmD5ngJo#|eL{F^zWjuI$5Ol4%mt`$$vYF`IF#R$WJTyT@^O32h{2hyB4HojL z=IPO!U?>rcovq1KN5(EAA1kK%5`D7*2eJ&VEMP|C>Q*F(8D`a)X#VL03k>m1L zNkAHLnrro;Eo8bcxh+q#qdYo0+G?;|YpN42;h4%E5#%GMLHA}^^K#ikNY-R6d*oOs zTKf3>vYC2QS))~Z!7^DZ^;7j+S>2+dJsMN@jAc!@>Domz^^oi_W2V7c*5V2pOc4Y@ z&2m{F+Cg67VV?A0FFQODt)hS_LhA#AAqG>-R6O0>B?2aUJ1mA+@HP&QihXzMZ!E-# zq+-`O1`-1Wo^TY$H%P1BC+l(AvXy{!WD2~F%yz-V|XHpgowuN zu~vo&Sm-lEyq$`rD_=m$oSu9k3_0TMZP`Q;2_lp6BnL+_nY043Ba-Y0M8!?EB{@(W z$rL*$=*xvwWD`birGzrPzvNQvsMsj6SU@2VVq;_RvG#bra2tW-;^Lyzu(PvOAZ$gu zd19u-mM60Os=$CnY#~P==J0utQjy8x?-Wz9ic0_80$1=&mM6l0ZU@iia|sfrfIz|% zl`VY*1_u6bD3|*UEfR;q-|PLa!lLlq0+> z3;A5=bJG;g?6o+|s`rH(IeRURvvLWFVhBoW|HJC9A;tVCm){0gvH3RoFi$b>Ld9UK zAaWp^nVy=Q`1J9^`|+{ScO%1Z-@JY`G&u0`MSowfw5R)d*R#%!r%&1+x3#uBYHn(5 zcvxTep!WW~yEWB!sw)5f>vqMho8@Jtf0o=RzFt&#?dp}wmo8o?IDalb?`-awoYSXH zW}i5IEbHhWzaKgLTV}?ggTJO9_~qxc)Rg3;{XZop?A!a}p7^-klGt54#UkMj0iVZ> z*&fY_+O`#rWV4tNTmHFu)5Z<|Sif%Vn((mDkl@upfdQ-h{d|4Ay%?Szbej80s+%jt z#o3AM=wNS0A`&{X=-C5hG6x_Mhp(KcU9@@YgLMeldR?axtN zwEd5V&Z~-T*Iv@rI8N2DD@?W3Rvb>{t;?G1!&v7sZ$i}`xn`>FQLpFZa5oZbQ!WU# z6N0id*ekoxJ8V?Re!96gG%&ojbC{6bC=zRL(qC9 z9AVRaGo3jGR+2<4BiUquM~)5rObdsjH=o&{j`7Fgq#-%QIg)q|V7l8WN}3~Mr^HPsnv zYBVTkuWYI`!P&3tY=gmCJ!#Glz7;G6+M758sp#>>(`c$v8b;e4Wr3n58|AJ_HBZ4M zKkT%FdUFzk{<}(CnOdM}KuhNHXeOR1V!?SZ4Gh!>qiTZn!)^dLP>z z5V6E{^;KymZ80qbv*Od_xEFz6_>=chubAD346L#aA6UNh2+PBUmt8$z7(H-o8%=4b z+W)tqA(h_^MWGdj)+!Cv!1vT~=&*>=JeMuyj@YH oWz4#Y8lWniX5g=81^=RXq3ldhwpWFqvq6_o40028>wtN;K2 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/11.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/11.gif new file mode 100644 index 0000000000000000000000000000000000000000..2bfc58be8c70d512f4371b50b2b98c7b8adbe719 GIT binary patch literal 4121 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/15.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/15.gif new file mode 100644 index 0000000000000000000000000000000000000000..c9f25fa1d25412e2e929347c0db0878b0b641d58 GIT binary patch literal 1793 zcmZ?wbhEHb6k`x$cvjC)UFLjcxo+2{sU44AEqro)?t{~@-b%N3XFR-Hwxr+ulK%oH+No|8@joUb;8o>khO;% z-pt54c=5$f_l$zz1xIF9ZC!k8rPSX?m75>!3YoF0@6_@yw?fNicKtc8QFZZ3q7CQQ zJ28K6dKNF8dwdf2>Dz5FoA&JK_I!S*#c%QM(+{R^st_tYd#Py0=A#ot|K1Au_q1vD z6!%ScSEOWk{yZ-7e1G7dQxZR(%&R%HD{|8GPuFK(p3ArTeE-+$eg#|BB~~S$dA0ra zCi@?c7ri|%pb4sD-Qd^>BZ@TkN^$;DL)19`Dfod@V3w#!8pDhc-E$->ITP|u(PWCJ5fjDdmu ze?xs!vs8YWOpZ)hzDjdnpQuc^mXOXgX{{oe-u@~|ZFy%{R@dafm z@IQa9kQKz2C85GpugPS}VD;TfljXPO4=V=Kgd@?P4xMV}iXHU$+K4vH=vCN_(f6?!dfWS6n2`SY^jFjF5#yp%}dht@+k^lQ5mUcOp* zcs?^%!Geg4gAHe92$#NpR-1INsfoGiRz$%hw=FUkg|bMf)@r}A%N=>H&t=-OGD)%dJ&%aS1;>?S28-4y56)#MguSwF{Wn{Bi0wrzT;!uy3&oA(_^ zP4_wBW87?f(kHzUYzsfa7D1pbvq83K2{lV=HFwoZ*)}QIN_Dj=yXlz=G?~}CDNBnw z0PXZuXlB-166sJM;cnU891&3uiN0ojaP%=)9kJ4E);wy(ATN9lXld$sVfl^MZ`{0f z`<5JUy@-h7JMl*sta~4a6oo18!%LOf$q&}sITYaYiet1X;M?G&Sqt4V`0s%?q;x>VWruo zITPfDMZ!^SQHzD;+gGnyyKeo4jrC#{^>Vzt^*iM1EyS8U1jSr=;w_HEA1-Lp7hf0JU8dSF%T&D%E<+7ylUKYC)M*n|{ZEMUu7fY$3aNE)ViSQzrOwtGm1);sbf z_Dz^%r|aYuqCS15lTnC&w5K1hmuJ8d&l*pEtR`qRKwZ!p-%_Ak4|D*~1b>MAGiJ?5 Y2vsx^3s|^#p`syH?V}DDY9U|^06Jl%MgRZ+ literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/16.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/16.gif new file mode 100644 index 0000000000000000000000000000000000000000..34f28e4cde0cd13acdbbd7482282eb306f5b21e5 GIT binary patch literal 6721 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/17.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/17.gif new file mode 100644 index 0000000000000000000000000000000000000000..39cd03538a5bc595fbc46c97cf2b06c8bd8dadd1 GIT binary patch literal 4439 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 IdD(dV7kW}yvj6}9 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/18.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/18.gif new file mode 100644 index 0000000000000000000000000000000000000000..7bce2997f86e477a6008fb1cae8dd03cda10abf6 GIT binary patch literal 3017 zcmeH}`&W`_9>8Dl1yK_aL;=Ob3nGb&N=AwoykI7&SsFG?;bls#Q?`m}L+3&8mQre& zIhILwGPi9z*=?-c$}De97tM;9W?i*RZLH05$~M7$)uy&if585*zr8;^=RME!`JU(V zdBVcQg2f3|kQMY4K>D$L6S^e*VCwsu-ujWeGlg;bq3@?|+D^5LdymJCUa~Y8^!gsY z{z2f>E#`2~Uw$8|*Z2Aw-rjh4oPRNYo?+rb`(D}^h4Ei=yw2tG^*=}Kj3iFKyRZM} zw>>}ZHeB#+uVQcHV~3jDI;&&G?pO8JbDnoe441zu5^60UJokvzr zv@PhaVNYFm*7q#wK9SmdwCvfV*1neFSKZ$}>n?nC)p6=?;rbSj_t$KHAL}3PEa<7l z>mRPT*&x4NGr#As@2yM6pSLjn(Y*dj>7NEpTfVv_{OyY4+Zz^dS_zZCtkypa)!&IX zoLZ*8lhA*0#ic^$@t)+qlbrW=g8yD6d*A8Wcf0xDy12e8^3j1`$4=T$Pfs(a%_2ir zt_T)IhkCm6j8G5+G2g)$PY6J6$Tu=1D+n^4RsxkeFTbELWWAf4sj1ugkixtW)6Ecv zWNS;}U`@q)+|pne3uOy!gtTyu*sY)rX4J3DCQt+`soayN)bb+?bUm^900+)sKxuL{ z8%=Mmr~F_`>6A0*Shk9_(VXAA|H04URGZCwbQ%k!hM3(&Bw}bBN2+@$iK77J6p;J1m6pocLCoTxg|5!y ze0Aaf=r6G|2gf1@CqP8x;N4093&QL$)a(tQM5EzQI10xor(GH%kwT&%jUNV^Cc!$Qr>))HJJ+FdjJP<~NCyx1LPaqE_63aOSL zgJ#3zZC@Ma=aC`_vIgR0DmB(FDmg1eWaa-$x(nHA>C)vM+xn(*6BFAkVVCoQt)yUc zi`;qCl*tU^fl7hP3x0~0)j?;Ir{@K}9Dr56MqFM(BC4y7VU+0E<@FR@0E;H2X%)v} z@f=?+L)W;zvo>&cXtSq;Arxc*kv?21kSdccVylbQVo%pZV7DqhphS%gSiH`C74Qou zvSgKjLh&Xz#rpvTd8hh^c&7$$;F+OHld7a#6W1_8So~tSn(GC`T+gsL@33|2Jmpdm zS>YrI@jDy0{X2rUlOH+5eK0+6lrp`Ee8TDsDT5j z{-{H!`6`2m$W+<;CXJK>VGkKG6H^utqr~ZXveDXw4q44brq@gD?PV95b>qfeAzVB@vs@)69ynJ_({9pf zhXVH+nX;txC(7&P0o&fLE>_-)ne&9@%ylt>um~*^gjS9cFwg+8GeA@d_$)LQ;uHZi zgD)qu=rnE_w5OnwK~M8frDxL6Wr(NuN%zy8qu5)>%nUU;MT(Z%M9@8KPp(5}(^nwq}4<2ejm;;1)1H#-RipPvSpCYvuGQVrc%(rF&-pS>snM~eV|J%Sv zVq-^GX)VgPl2}_>75Xf-Tx#V}ke21+QxWOo&{t9T)y@1L%_lUNxjB7y$3Ez%ct$^V zbNYej{G*ulPYDA+6bhIzPzGY4;_7%OZ-TeLGlR7lNL7qa5<4|1H190@IRAMg5(j8}L?(phY-y`50aH_-& z8U{DVAdq$){Y{AivqB20s8-wpxRcT?Z`FaicaB1cs)H@ zqu@!rE~yt8sHR{M}v6=UaO z1;V8&kz9rTSSvM(4H%a_|H#z?Bv%Z^7s2BH^hL48W|DP0J3FWiUv6)uh@L!>i#eM%g!m2b=EomsZoMrFNGwm%(_p IDGd4k2Wv_t&Hw-a literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/19.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/19.gif new file mode 100644 index 0000000000000000000000000000000000000000..adac542fd33e152fc08baf165d1c5a467f0c2747 GIT binary patch literal 3040 zcmeH}`&Uxg9>5Q86bs~`fKmvGN=OJ=XsCdSFDfllZ)I9iBDJa+pA|D35dp&{&8Do( zY;4M!>h`{Mb4u-@Ju!RP^cd|iS-0+W(rKJY^ny7y&FUYxYu(?@4||=p&*yvg=d)wt z6ylJDZjc-F6M$^5zP`10c7JD6)#SchlXsnjv(6| z>EJc5=N)_dp6s#Rns#?z&_7FEhJL*;)OV`2igr|s`EHfycxjOBUP9S){P1tLZ9k?D zotoBK?YlGx{p=*K^F;QQZAq_hZ@zN8_Vs1ut=;}ZXX5Vf!w&stkF7!6_vF~~Q_e42 zQwFd5Z7rPp{N$7umsj06{^iwb7u)?=E$cCNcT@WxZGL;VwCh{n!S+dC&tvtpE@`a} z>AzoKJ09@5IkaXOckr_N+m5)tp0@s5Nzbou89I^r`sNqsEVLj0A>Wii8n{S$u_w6y z;-m-7>#a51!Sg=0j@TRfa~i*`czOR^-;MN_H%xDu*n0ImrrWFwyHC$LZSv{uP`$H8K03&4+LrO|lK-RYjlXxMJUUzOqWi(CgUsRKVXtA_ zjOgUqk+Q^SAzy%iK@jBi8xkdi0K~ID+lS-^L6~73(3coWjV2JaB*57@AYe(9xkTZN z;gUT)$y{i;^9ECZf)YoQcu0yM3<{+QFxGSV)MPOQUzEI`@%O_e`5K6ki{etYdJ-TM z;*2K0#2-UvmP|bqDW)S>ja&4r@f$q3$u32Ab{dUSUB|Z%WYr@|umzzS z8QeEhTIEOcJk%^~L;JWA>!g)T<+^(M3`J&BCNvYLYXAONR(5JEH6pX5PP3yhWsM7pwJ6#Sc%<&*+cVb)n|V zh-4;ne&?aZnDT9c#FhGX)TQZ0^OACDKVgbXM=viAc2#xr(0#$lKv*R*bgH0gibqRJ zbzDV+@}yMTVz^!khmDZOJIG@pIE00ecJg$cSRz3KBcQUm)Ka6COO6(qHUb{N6n|&g#oQ7BP0T2A!<>-(?d%YZ0Y)3g*%^dbs`F=VP_01?5WkkRBu3`YSH!tr@T1}2Wn1C6=y;p(WM zWE@W9o>0S$Q{{;k;1YeipWs%ri5L|d|K#7jL8K?~x~|Wy-Mz)(-0;z7` zrq|^&C|BA1fvl{xrB3eEY3+&ZHWU{hzbx~cNWxl5Xx@XQzMs|^32qyeiQk+<6h}x? z_II}EitEth^oI8CsGdy`U+SRBH;ZD-!C)=1mzno+V$iIU%#{_E@PxslwJZbq4VGTz5S8EOsyqc)urqel zLWyG*BFPf<0W+yl>L!t_yn1dTA@k6anYFyaiw{<@{N!+aVi=*V?=k+epF6+(RAd(( z2A|h5E5En3wRQqb>vyVhPme{ilEq*Wj(NvoV4v$%pGgY{aO8Bi*F9@9+vY3G!!EQ9 z12dT;M{Nw-pPfBwCg2DZum@_nB|u?M6tE}Cemk;o2C_C$fvM3wl%!KSf9 zA%G)N){#hEY(p^qk|_q`<4|=5kCv+8YJ(nzps^Vmtng>=7LRFx7yXhJHOCRe!9jzE zcNW7Crh=jKD<4PUHXvs|!gh=#OaH3QTz?@FKFxJVTePUyiG&MusvD@%=~@}t`h^1` x&iYmvvfx#bmpT3`PtF^0k?a@iE%))*1NVySmZizF-M!R86}DbhY`;HF`!D7eFhu|W literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/2.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/2.gif new file mode 100644 index 0000000000000000000000000000000000000000..7edbb58a843cec27a1641a5c9b6d30b8fc1f8c26 GIT binary patch literal 3222 zcmeH}Sx{4F97iv?30p`p30s7afI%Y0usQ~*kRTvo39F(M9fDyo)qyIGxZdPOAXfuw zMXOZ@MT%f;N3m|GEf6fID6)v4qTr4|g<*?Gl4b#gPG5ZI?R!4|GvE0w;|8(3`Bs1x z@CE`_yJD2}b|0SX8E<8)R4Qff@v))So3@t97s9rhpC4WH_pkwL!xySPHq{)oZ7p>h zzvrkPIQOBeShx`LV*^5%D6C%`ebEiEl~ zk7CE4B~|Y;QY-4zgO`-;+$Y!56kQ@|0&T3P_~I^%dg!{c*>mvT;;r*BVqy5BGo%N1 z>y$4yj<eSIAsOnz$S)c;g7`UNK{5a%gXzk9w@* zb+!N1Z*Z#q(|tE>A72qE-$~S?mF?$QKjr5tH)lqBI}f!)-Psh@T^p)=9MN)#S6{a5 z)5GBNdp(u?=bL}eIw4v-(&%^nXp+1*wON*U)QfoKAiI@LkEEj?KB(WsGWwPb_Q~x# z%Pj_))}E%~{$aCw?}kb3!SMzc<>;fP!fzD)jmqa~!+lLXRgS8nQwfW3qmScXUR|)( z58qz3wdeK<)!QANf2Y5%2`s^ZidsvxTJ505@&aRHmU1EkJ(zBw0RR9FAJG_700J1A zx8_Jz0AQpRL9k>=GRa;_N?sz#%Zsq5<3pC>LV|oY+spD{Qbe2&7lU`gW43MM#*2ar*Rwx+$e-nGQen7iJ*HH&6s0DE)T16NM@URLM4sVjkz-7 zEVM@ox@)%#;|K;6VBO^yGDT45hL=an%;wJ9`Sg_goSH(sqv#czn<8!~#3sD~%K0_n zM8E1+ZQZBKnoC0ED>;cRViOmBxv=BT@LrSCmTmn8?91ocFUGXRDmGPv$5s+|%)PO4 zu!z}lmB=t-ZS|%1LinMQX;MOwcmS^G?9*u1hBP-+<}$vIIbo+9TL7Q-4AIssuw z=N}TG(+?p~3br$0y?OA2c(7hNM6a9{%@o_D&;*eH}6v}Ak*0_@l)tisouy=(k7zah6uVrZhe;O``p=gyk5s|!3?bZf88ne*I%R2?0u zS@(sfB0s%2$%Th?;siz>TCk?_9ePuuvyJQQP8vMj7X;0X5*K4F3?nP3QSNhuu{urs z84)e)87pS2n6YB!ulU@SnZM$z3t}))Q*A*^DVoWmscAMP6tU3N6pmY^9V;nttf6+O z=$l~2j(DK-Bo4lc`8k zQg{>bzl{g&%nN?YhqNV#O ztd%y!jg0~xk-W}^+K#6lpv-gGpDC^eLab>noeHkN6PT!JRZZ+!1SKWgcK&*DT0tgB zXm^Ef+`*7`+buyz`jvr4Q3^sy@c+#WJ(tOup_y~jhsI<}!D%hkERC;dDT30s^pD^v pEloJ|-EsmUGDRV$ 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 TKb&t*bx4GV=Q(`>7FhdV^sO<; literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/21.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/21.gif new file mode 100644 index 0000000000000000000000000000000000000000..b98421282467fb377dced2eabd72bcc5525f3e8e GIT binary patch literal 5191 zcmeHJXIN9|w%#EDLMRCx5oHq)5g~;dB9Mdx2uKqUK~NJ&fFL1(1O*ETN)=1&;5bqh zMjZheJy=FUQGrpZ_5uo{aK-|HBPxco1LNqq&-Fg{-~DmVPM&A){q1k9^{#KdtN8l) zklfftfD!P12C%cU!{Kno#>RL&-ptI5OeR}eS`r8ZQ&ZEWOP89Pn_F93TUl8xT)2=% zqfsc7&!0b|(dfmC7u(v}IypIEFc<>^16Nm9ZEbBLk?7#y;OOY+=H_N&V?!d5bai#- z&!4|!$r5L0=Xvwyd3bnOSXe|vL?{#r6B82#gYog>#}6MqczSv!CnwYC^kvJI85$b; z`uavjMsnifHm0Y0dwZ{ni16|8ad&su)6=V{sK8>e@87>)yLN3yM@L^@-_+DpcX#)V z8#kh(qj`ymK|w*Gp`mecahozTy}Z1lA|n?pSm5vP&-C?8OiYaDax*q;c=6%|o5k|= z@u{w^j*E?TadDAmWu*!P{(gSpVPOgJ@uJjJu}Jjk)2E)Ep5&yY)lpF_7R!&xR@ z-@bjzrcHUdxoK%>H*enDyLWFuKmdosS+iyho6Vk{p6>7OU%q^KQBl#YTenuMSW#0` zBb7*EqN5)^eE9hB<0ns^tX{qP-o1OdIXRh`nJZVW?Ck8kdiCnNckdoOdbFdku&k_X zWMt&|^XKQ!pTB+kc5rZTY;5eEJ9n;KyS91r=2fd!J$UdSGb3Z)zJ2@m?@vlf+O=!f z!Gj0e+S()%iAW?03=9+sg?v80tE;Q9uuxrH{q*V6$BrEv9v)6lPahZNfCMqi{8yXt!-@nh}@d^qGdV71fZrwUQK3-K-HSpxgxpU`Ig~GA1vE1C;vuDrN z)z!Uv^(rwT;lhOrO-)TvQBjkVlc}kx8X6k<`ubEVb#!#}?%lgOIy#*l9j&dcfBf-B zd3pJ%Q>R|Pek~9P5)u+*GTG435DJB2GMQRhT3a@64hsuIB9Sj&zD(lr4jed;mzS58 zn)=gEKh@XQ7Z(>dHa6zx=c}oyy?OJ7%jL$z#BANXxum2dKQFJgwibav7#SIrmX>bV zuz{D5kdcuQ%VxKU6JY15M)!yE|Xwf1N z1OWh;nY}D#G=sdu!|1dSZwDL!4ud{GOOb&gAcHMtLw9#dhL9b%o+}1pxe2@!5A2&u zO<0h}@xZQf4kQE$>D)vfb5j~OY*SEp+@|$$ZX7Jd6LilYXYhr5u9yvG@RL(SdmnDcl%E!Nn z3%&eJeQpZG_cVyuuo=bY=}#X&Ouhf}-Q>jE@v%3rU%mX}pQ9r$p8q~P^z7;2z>~+1 z9zM8#@9v%3{e8VX-CehC-uSJvqy74|t8J|<&GM!zzh1s{@j~N2&Y%0G;cR_f?U|oX zpE~(dP4$W6Rh1RxWu?b{Jo>|tlH$XM4j$OQZ?A06?p-^Jb`%!m=jG;X-qSi_49c?X5lm=2=O%(xGfy#r$2ZSmIfEp)MDWDc=1OUhxU`E3%k_DFna223?kX=U4 zM+5FQX;t1u^L#=Rfw5;#selfL!>*|6)L&wKq_M_gTdA+i0TobRpR1{D=lDyN4Q9v- zo^R*=2&BaZm=Ks}s1~t9YC1Z^(k=2R^Ej^e)bEyI50eSRpS5A}1;_eXK z(D`duB;Z2EpNsR)noGoBP5J3o{!vsJ9?9EW5oCeSKp`?Kw|5qv-?DIcDj#euHB4Vg zx&W$Oh&J>$npFAd;0V@=1c&|!03u)VzIf+)) z)?&#IX#KLvOWgz*tfIqSwgw|~S=|6-c{1;LiHa&h89EJ=GIZL27N8DjK%pZ+p_}gY z8KfAWyU4Q{N6W1xn1vdT7Bg5j*~rVtM}O9~s@u+lGYzIX=!L;5i%gsnSuLFs7;KgY zv&z2mAXdo(W?fnHcBH;-OnY5KE=w)r= zi+5yi(?Tz=3)$-#z{<2(TF;_>HLLVN z74iZ=+5$75w-XZDo3!9oCP)kRfR3u#)VO6`6)am~q*q4WYsn~-7y>Wl7|g6VOnC$_ z00HO&C`i0~7Ig^AYI)KTw(c5v2L7wt`~59`OwG^tp^0#Z)YM>)XAqT4hGZ zfDqpEo2^g6==<88HzoBbgh{W6MzKuq$J|am(Ak8T z7qc>8Sfl@hE4k)q-RPU!O&B1_XF_+s3zOugSLsX_C1U<@Ws4Kz96X8d|VQ5oFLC4Km`6SWnWJCRc@;5vZr+(y!<9Z(RFaG`_yqim#$?8-5SI zuPJ}=u(w{#`hHzAGaKa2`1VpS$z{cl^Q>jXSY35R;M=LL0kkeu;2ja8ZLMwwp=)DD zYwtE?9TN789v*$#WCeGBX)w~&)3xTf^<>y2Mx_uSjf{uuqI3~SCs!A4N=%||lc332 z?(YSGmPp%pLjdD7Xc~$($J&BIxVkP(PaI-Wk$c>KkC6gSpt0u?Y&NP&)N@?h2_0Kp z=n|#@bNsDFOuMY(Xs%*<0eRw0N2#f%pETB{d%$eN>l+7CbTXlvB)SJ_gEiaBI#;?z@CEe9($%a!yn{{%xZ6V4h|b__5Emu-`QJq{2IO3=co9 zZ}$2AMWN*n)mCuC>u6Vjh0I^m*=(>~>J>^slN_{=|9-4<0A&!sOSElm#RQfN8Pnda zT5WC{vY?33dV{j2ayOw(yp4T^$+@L9@nTPLg~YDKe%c~d+m)CHrcb)QFkhPY>&MRx z2MdFm0u+i5%X@d0S7v?ouP+%3UJ-({ij=Re!5UOVlfuvGE$TlYUqv&EH?db%M;P=2kO*LAt_IH~jgpo6v#dPy4J#E5`_+&b zbGpGr-Kw!tl-JD1G0aF8)Z1oAQ<&lx&AH~=Ur%>WVEqp47AlTQ!EV2%Y{j2PseQHU zN+b)`yO(_kz77&{gT)S_jlD5`CfCK%!iG( zNPEFJJtdF>S5zFQSDO5%R{lS$l@4=l@&B$?&Q23w+xikHEtWwe0|ia9gNO`mP3x-O&QFJ!nzJS0>tLe>Afaq?fjZH2@6bB6Em!&%OdTG4OpzSW zvKf5ZweCkfqvuof+bMoINu{1_n|RpjI<#Ez$`KHh&K2!BRbR)NsM!JG`(w@c@td9n zu$caTo+w8!fFGu58Na}vd0Hr@b3aaDvJV$OG^I{cua@rc)deG_hRfTk3-rh=Kw8iH zW}5kG?LWix|8kgSmRSp?EISbMtiTY}Y5ABrMeSunmx__UEFk*M(YMXic}%KQ6^ZGV zs-VWUKhwF=xKfJH+1d?58p)+CNaS#VmsYy>P>(u%r;S`jZxMD1`24{hih&bB3JY5=_{)1Y}iDzqL7DO4o@Y9LV|BQ3V8gQq z`;RItYB%`(qcvGM-TK)X_tRxUL(i(8+Y_HYsCjnI`{9Ym`%Ub<**-trIdW-VjG#ZhSshwl2_A%Vd*s;+52po3#knqfY z;n{tu_ZzvjFuppEV>}#uV5#4u{?80e-qm93!FtP~y9Wk)sxGNhv|GV@E%4wW%flbp z-u#-;RciY{>!LrOHQbqFxK}>hyREsz*>E}bkuLJ_ZOPEp*~THw;|sCBoC@su(tYUS zbb}^JS3Tpwh5X^J+=ulxU(OK3x|WCRdhb`pL3EeQ-^ zmTV1{tUXZc;!Lb+BU`RPIN_0Ygh;FnNL)rNE#B$2d5HDxZ1H%l*w zgTe_V1C!|jqSP~-3w2|UBtyRV1(Q|M3LoWotNf1qiVEWSP_ev9*X{1>0A_p_Ri$n! zCUb?Du5Bx$V`R?Mzwtcd_1{Pd%%#(SVfRd|?3l&c&w&a)PQ{!IgqFI+B5K=oORfqS zJ~d85cW@dIbhoL47S?{nlWN;``US*qjS}^@BK#RAp$s~|SwRV1)MZ1LDu57Mhji}+ zY$*gpZ|R`_xCj8~q$CC@%3A{lWeP25+KNfUaGX#+n8%VF0N6s_Oh6M;G88y>vTbJL z4#U%8Eav^=rJC?sUNeyjA3|l+l180QHek;cq>1%|8Y0T;=qtPw)PS%9CSGBi$9VlD zm(C|lE_%)@VePYp9KO~SYvQFJO1Rdz(VZKv>r=eo6%lf%)-Cc0uPg{Tw2iJ|BPxpR zqKsP{+_x&oAHVIW+kk*RjwA%^LV8a=bjK+H+IEeQejCQl)mz8nr5GperZ4qb5*24& zAl{C_lS2?}1LV>x8!5IFx4QW^tB;NFrRJQObM}A!Uo_{;JkQMY?Cn2)m~&>%nK@_X zoSB~+|ETvf@V&ZE|8eFSXl3#s7ytz5fEDUN)|fnq9HuH%*7A6j+%-v_@^LYooc5tS zG{sdeq9jEGCCal|5%}crrK~i3u+>!gYO5%95P5|IGYW2yHCTyrR=~bU*v2q<*zD6k z$Wmit{Zf&YehCSw(^aXdo`~m-lHNWfq+j857s1WDKNH6F4fIM)cl_Vil4YvJS_adU z1u#8HneuTVlAM;O#H6??MdOkp@R7<@d(b*ovC{s|gOri3Ks*-BXg;#Su#>(F#wzH+s2rZT$Isq>}Z#RDY30wAMj5F~+N0-}JS zWLQl0k}C+gcFG+{6v=@~B`S%#Vc!zi4_ROF*}Np(o#x;288m{AD(p0Od%GFlE z=o(hy^<+XJLYzR;Wo;!BljE+3-0X)(k&?jIC_=eHxk*(}xVfl!ixM?_sPr>wKxd*iYW;q% z`9O~`&xfFzZ^y5iZ!e@KEk|%zCb`Nr35#VgQnF6ik(r5zJD1sjZXwe0PG}16Dv8NO z=0YM$Py{QNkod%FG;v%=(&>q3VuM|pH5%ISPl-|YzSrrZbz`asLoY_QI=QClx?@2H zKCt3sU4>TZ>UhiABR3~nM&9X*Pbk%>y>KpmJh2W~09x&{@Ko79+TSWA(DS3#-3}GV zaOqIF{i8|?FB@-B>CwE(n3;!(r}JlV2FUgp|75nRE;qBqqMpq@Oyl)qD=0BZ9yCnG z$?Xw>8y0{YTUx;0NCfM;#}}zEf^cy^uz1W6d5;<*3Q(ugUR6+9*s)Vm7|^>W_54i| z)&?KW%i!^muneOGO~l7c+z^jSSYEUx|0M}~azn;#b+(lchvQkr%W8HFgt%$E18Ne} zD>2WZlm$Y&$qxA3b=5pv2^h4hAy-wAQ%sA1{3Y#KsMK2_5V2@IO(`H*M_b$NewV_H zMkOpus35pnmGAN>ozrwyM)bX01L)%fdq@`8(9%r2oIrIeYNNGfAb36=S~XGUhID)V z2_GlURkv>DCpVUyZ^44(&>4qnIq4>;SNuZiwP^y_SPCXPkXtWm7Gmju(y2*{X$1hb zb*VQyUnHT-)2j5Sgxy4*@=yi!^a+4Cj8IjI8I!R7616e^;L~9uh7~$SLB7`aEWr?*m|`WiZm>s5cFiv5d&UDpQo;v<>?#zP7Eh4aFT0Bs$OyFb zZ}5va{31L*sNP9TW9fHzNlg}3p(*3fKP}FZVije6GiH}~Zcw-V6_ov3VAbxuXI=$n z=O&k}vCJjB046yMNH%-x)9WOGw@ttt-%()Zgqag&PS{&|k!YS_<{4(5VQ=$Qy*Xj# TguQ+mZP{ha!SUN*Q#T$ZGKvWRLv@}vHJ9z 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 Lls229f${$VzcBA< literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/25.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/25.gif new file mode 100644 index 0000000000000000000000000000000000000000..0b4a88322946c1b65e9236657d6f42c71e412a94 GIT binary patch literal 3127 zcmeH}Sy)r~8OBdG60(?tEF?im2r+;L1lbWy5OoM5NLU65Q$b)@h6?I*ijH!^Ie`Ra zC+-{I0$SlgMa2~iD4?}c!BMND2!czsN^wC=U``U8YM-`Oz34^na(Djk_j{LoC12#{ zpTq{(z%U3*yz6*VPh5@91jVmp9hk*h_ue zPJQ3W9I7V2)w{mF$m+;*>u>N~s}C$^0`3@1m;r!i)ctaNxQyEFz(9+eC`^XN3!8BQIW zt(<1eq0t%6P)gK1?i~Ktd>1RgD&R3clgnP50)~j$^r-=)g}=2U`P7FrwO!vqU_*lU zFw_kbR;~X&u~yc;$&E*};A ze5@dpf)h2mKgvz4`5VTT6Y|IUpn?~B`O!Xow+E^_0HQ)fRwZ|q!mi;yyT(Q|dNO5( z(vaIEBo^5_+8bJG6foKk?C;Q)qcmHc(BdBLY_wYC6rS^a^2noz&3GFqSXDV4J%vAC z=ygah&ds5MJuT_7^Jnmlu+);;JL9n6_Cf`txBd`g4RhfCW$xgc}fq5F^bFhU|C@tyaSPrXmiWx_-;j z5{OH5aS8B-y#rjF!nZ?Npn#VKr||^yXmubK7N3GoHlI6xp_vXT&t-${;6-psCed4Z zju{@l@;WGkgg5K1S4V?6;eLXa^J0PgH(&%T2jlBr%DGT{sEszJRRQMz+ZQ1&fFZ;L zTCD{?98kf}EP*W8;ThJXyzh2EtH5btM3@+smE~CvCcpxi0iQZ?p|$PAT1a%k0lb8~ zN*V1XM=G=P7E0aLQrUW{8%L`2wu_dUPcC+qVwegWOwJEdU(ghkP-diGnZMYcPcW7@VbmOIpx>pYmRCzed%2X+OamT;YIX&wUMpf-{_n-e{s zN%$P^C$0a3oe}+cXRJY+JA)~xIrynF;Ke9!5mD7YqETbKiJ6Anb{rbZpvC2^4Zc{! z@Rbq`?@=ki6uJ~mORT8L#Gz4nS%;41Un!I3?CJ-SYA;dAvBLTW)x%B-Lr`6rn?L$k zUQFOV%IqF#gC5}Bdi>;-4rr1qs0)dcpyspYF!zUWfX;B1Pw|1=G5GRA`1$l9>H$VDpJQo!fe^lQ++|HN)tdbJB?X#PIO)x4gJ8!2~2 z#cM>{Dvru&7`$UFnDf{c`}j!Jz`M*RZyV@ z^7Pj#MM@!7tHBCOaFE%PsLDbV6GZ%IZL;I;y%5Uc#Be!-<|=i7Qv73~>`5Pbu))rF z4u;KN_alhm#Gf?TP=UVBjrl==;SVbw#biM?3VSySG~J$pUOaa$xC~86cY`RyE~c(y z;VHb7q8o$qEoz*V&^-!AW>ZrGnNNZrm(c~e^_=&;`>AxV)ar!vH+7{Dj`*x8YjbMB zHIxKdFw=fb%KE~K|CGA|=P)WgdLGg&yjf|cZgHZsr#XXx0-Pulm#|#GXl8gM;4(!x zK_DnH%g;`r(Zn=Kf+dqU&=N`%R>FP2!cLyzQz()2zGiX$kx5}ne_g7o0A|~ha|cB7 zLV~2(5Gkyw#S&tyLY|H|Y{a_6+>X-?Jf!k$DT*zx1Tt9>YjOOZusexEd`ajc0VncO z5C&z(?hjceaQeanUKQm*8~Y?)J-YnWH9d5H%9D_3Gk;f#^_)%f$Z?0hy@IC_R`#iE1Ud98@p~5HAYTDuK4U(zc<#GppqcpSq`GsG$aR6;x2k0_?6-$i z7m=~qrPV9qZVTWbdZ literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/26.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/26.gif new file mode 100644 index 0000000000000000000000000000000000000000..45c4fb5563081d684f00cc3560df41b803cf7bbf GIT binary patch literal 3291 zcmeHI`#;p_7awFU=3N(!aa z)@F63%VJeZ7o=9ZQnt0-6xqs9JUi^x?ho7dFZh0*&+GMhp7S~9ywBzRI**T^H`7C4 z2s4C@Ps2a}4)C#HI~NoigHSB6*8!HOC}Tq)*G;fC2OKPj(FGJVaMc0jhEI`juo?%# zaMPRdAVYurYr<3!zjB>V21i&gak{lAP5J_jKOvfu-^l)FrbJJ zYMHdu?mXbW<6M<|Y| zA7M8>`ZC!V2bA`phykRIr;IV+AX^n{3v%H74Duba8K{B=jqE_49XJ^VR%_n&!S)dB z9_rY26KnuO1C-jd*p-SfdSR<_g2Or}K^W7S`c_1>>H(0|FnoE2Dtn+MKt7mk-&j=y~ypsn%lmxoVC zdZ5H|I>KavivdiXQX}mqD?~$B*i#4WNk77aw9K>q-nW-Wl{i>Tyn5Y}zx7_pjuXpm z-amiVI}dkG5I%)vQPqK4LQ?mp&HY5Z8&QJyH1vm&(Xlk* z^NW`zUCN#(D_Jix)u)Q(37JxkRKCK&g@>X)-I7O%@1c zF(Q_FKO=S~L$fN%L^j!T<_9!`=_ON$Aa{zZQ!JH6gXnY$&CQ)or#V6{RGN!3RsGVP zXl^WbI?IIt{r!;DXbNE*E12i~Hba%90aga>y%$4%zw(V#U%Kf!(Zyzdy@QnyeH^V?};EHd*b1A{Gl- z3|Bs#>dBzWidmzAMzk)=-blj~^x{RO9d8z5QeC&FIMMSHmw~Jb(7|_o3gO{5tsf(ZIw0Uw(ej z_tX7*y?1-MyYAe+b+hxv^^R*-ueATz*4pyJGV% z-LO7InXFhRmq{f_iEG6P@o^$yY>Xg!&FZMgi14sgD_1NJ4G9hkTo&N})zT$?zCPZ3 zo)_1Xvv?7E;R2QilR>co;>YZj#w?Q2>&na`qI z;P6A9*UbBTGj5qHeb0NMbcesk>yo7nt>&=64j3%Gu)bZ(E-AD_%hEo&CC|scp>@Z? zB&#mGyRfCTjNaG9(VE+vk+&0#)yRV{sBXxHXUVS)?Mav<;AeZMGt;}7F;7r07;<#B zcYzt6SxCK7!7kZSa~aA~zn0s5Gh?nELQ$(3%P%ybUKFn@4$Lx4U+a6kuT_ag6*8M`RjFL!A)XSUC?DQd$IFpPS^kjr2)m5Fadb!#?~ zU?17hwYm<~GGKB$Q+BxvfZm2h8moZb!vTPlnm{-Fbvs zTiy8VW>&3@PL@xR?~6<-^j{c3F!98{F|xnG&BMSuQmipmE8=;oyT7A{X96tV-m~NL8l~6+F7aUlOIu@M5({Ic8FO>gh^2MGk_!BcJ BI{yFw literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/27.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/27.gif new file mode 100644 index 0000000000000000000000000000000000000000..7a4c0131dc926ee932804276038bf053a5cc1634 GIT binary patch literal 4377 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 G^ZyND2?^l< literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/3.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/3.gif new file mode 100644 index 0000000000000000000000000000000000000000..86df67b7aad1ac80079562c74945d9c4ced3c397 GIT binary patch literal 4017 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 Z`!_Z%27Br#6>oByc-ZnwM>1&V{{f6`u%rM0 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/30.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/30.gif new file mode 100644 index 0000000000000000000000000000000000000000..b751f98abcf8173ae3c08402c99ae885a0dfe162 GIT binary patch literal 2555 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/35.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/35.gif new file mode 100644 index 0000000000000000000000000000000000000000..a9322643dae616390d8263e8cf1bc4d9fdfa03ec GIT binary patch literal 1800 zcmY+EeN*;s8lPnTig9(j$s_FflJYkH{TT za!hS?Op8p-QyqNCgD=nyq|8VqAWBe7W=Lj=WoWv~dM-#W=l*m5`h7p&@Avn6Qq$(d z#V_{(K46dnO!sW2Cz0vTJ6SekbodZ+!@>-#`g)hY^Q3xY@JP>LZOIHSGgy9lTi7p- z6*2g1MUA+gN<3J-zRU$o*HRt3!Qyo7ny6+aC{39{v58t5f^S zS+91RvFjJ!Kpn3kxW#~ya;k(?`0}T$e&S-t!;oz2v;Gz4MdgyIpG!F91yM ze8;D&4irqgdA{ti!~4Ai? z9gXWdv&q%&_hy3NdPAu5sPfQy-5*Wf!)@7j`*t%w7@BKmbQ~%Ed~?7b&V98DrI!!p zj1FxXzPk6vV~c*jIO|@=%In7zzqR^5JSXqk@5yw(IMV9*phL&}RQ&jxX%BlBk3O{T zGY7WsNNKCD9QtzVlOK$aFNP0a`{>DcG0oL8@AgoC_E)#;kh$INK)2YG@^ZGG%uIP< zrk2M600jQU7bpM)H25Evr$5gxO;eWg!`4l&eH#X) z`i05letvwp#Ufi1^ud0<$s*THRfg*15>r^==cuSKnk$rfXDfFvHCTjPSz!^HEFoJ7 z{iH9a7U+vElETT*C#FQfO)#$Ht3p-$75aHx1t->rDlXy7l`iqJ6dU4FP~}!#`D=Vf z<>2KiB`Dmh&adF`L>8qoJgH<32pdSM1NKmdp+*>6dO)^S47>tHZEIVSYZUKrZKAaU zHzzd(#2jeI&rcqdcy4M*s$B51xa!cdBO2+)ZK$%|qp)Du&aK=vC8}kcw5Fn}S`t-b zGTsPE5&CGh0U%0x<%=tlh}R2j!-rZt;%4@)sham_b-#VSN_2amUbNdDakbFi-Xrjy z7C}gMmZBQD^4)dj`|AZBgi2f6(0KpDtnJV3+iw4zdF|74Q!Z=gIFteU zXKG_t#}yBS7)c%x*H{Y!Dx}=RS+*A!Xi5F{dsCuM^q87m`;-2yh6i}S6Nqs&g}551 zLVk#bd>zust<#X$(xedX`8#Mqa&LW`6$pIYmvt@9JnFC^T zHckk4W^RNnvDQmDjRSQve|A#ZX#uOW&P7SXi=4iGDnGOJe>eh+LtvcL@g%AcO+_1! zAi>Ev#;fD&|xlcB#x7#Nu!1@j{Vg&O||UrmXu z43)`B{Cy+f&>O~^p7}wfh{%n%If$C-SCm9A=fD7Ag(n=!q*U?w#MLH4zAPjwU~CVb zhQq-Iq}Tvt4d{>o@4;B$R>(TQcg;G$`!V4_Zj}?5U%@TO;N_t_UWS0Hfd81F)zs`r zTOxK~r^TJNqE4}zM9EMsq=ao$9}2(#LlRLQ(Ad&zPl1OL>DTZ(h0zwi50;oYOQxPu#WdW%h6fe6LbCLrg zLXf*67YU-E)h!@6g0>ohAgCasYo+5V!mgs!^|B~p18ktPPG`0|)0g}1y!yX5pLxE& z2wAu&Kw%Hq15Z`J+kwFTW8B6R&xh?%FFNQyGzraj{M>zgeIp|0KaLdD);&5$n7zrE z9Z#Q~JQ}OaoSn#Nsfb)FBDY+)I6u{y7G?AE;OBF%zbT5qUe0$vsMl}GFR&qz&9_)D zF8Xxj5P$4UoqfDHeAA})-K@zr?%dO_=Vu0=on=3}M9lI9=U&zKRmIJ}H??JBu9q?2 z3@4wJgHt1W(-QdRd*Z)sU`%w1`wo-Fdh=`deKFY+Wxhe%kgb{>iFy84c;@+qw>{F# zeSbQWMecwC?zbP9de--$mnsS5O!sp4qF&3?UfiuR-*#_Ev$|93{Py;`*@s`e zzn{ooih0@X{BBrgC|Ef^KmX>c_lI7_dCju1p@a8M*bQD%gitrn++FkPlFQt*aq?#R zbdTLwi)~AW!`RiGm(OKAHwNubjfjzQUK%&Pxs_3_+gz$uSl(gH6V|L+A14*dR|!1@ zNCW@?tQi!Z3a9|S<=cW}4*=MCNTq?dZ{z0WZQBm(^=mR>X_Q+v@ zaVv;)RwWDhs7p*#D<4|oWfEb-hJDYuOs2?@<)x7 zF747l=g2VmKZ;Ngh;}Y67E3}4KeR`Fu**@DmVI`CdO8+D-Y6r&=GItsH`RY(#FM5;+T~atKN>WX6impxm9mHRF}2!9&fiL-b1VeV}T&=M__c z?Wi>lOv?_}i*{Yps4wsP+$WQD5y_xvgjrmZL7L~Ovs1}z45$JEgkHilLP|xq_LM;8 zWz4Ijg$)wZ5ly>nmafQXH=K9CXr044bSd{VtGD0V4{qy&au4k|Rx?NKGd3b&&mfcT zOjlKJ=_qW4`Y?3jW1jT}sOGMr zu{h)f5b0Wuu7LMsRSlm>? z+z=^4;AVSUnlx_@*)MAWI~;X3S_8vpYXu@ zU{Z9xkoKCh%i7~2<}M6M?X&DaI3pmb3)*zqK9PF}(b!z%t@^cyq6dL?$@nDFCXkgV z28lsv`UNPiQ%7D(oIs5bVVT_uiPMFxaWn0Qm+ukc6d)3#U0v$k^4th@=`l&KPs_yP z{>pyD%J#~+CsKeLtqgj6C7AL%JOVtKyhRU+hXb+@CV-N<#eoF)9d09%fOLTdzn*B4qb8apz*bKxqNKOQ{Y z-?=-gg5x*W@p>rCM8_`y4ad5YF^;q)HBrT5Pj1u!5u-KqXs}T2kV-PJyxyZ~mLa~L zgP6rB*L2Gw`1A*te*p2ky@5sqi!04z6IWzSm^xYsZtlZ^iLoR(0whvCi$7Pn+KL$S zfX{evf{CFnNKGQTR~olUIB5;`8N%kxFWFKIKPGkSsO#xajNj4(ZwT|WfJXdM zw~_q1KKGrqOH}mZ!P&LU1rYTAS+Ww?5O9Ynxmm|!`*9Pw8d!>Q+RJ_LW>l@^sa%Hw zA;Wku-oaHFMv*f?%Th!!rqWZ3OG)h?{_myy;db3`EhVUe+0$JSf;emp5FbG~BE5$l zL#3LZ@S=!p(2Ni>2Xr~H990%~vm)XAatF^KD)yB-UZY^|TezlGHQHddWJSrx;^h4cxq@y0XFI&S> zE~nz;(St&iheOJb2jw@#6_rvdBT=G6WX`tRb&tFLx&PjOz8>$_^W_)d>&lKM0wNek z!1Bx8szKi|*;>`Rw8?tYXZ8D40sfjV?drL@3u)o17kLAvOl3A-H5#i?KCYLq6J}O@ z79UoBy4oFLt5T`vd)<_G`H#)<3teQ@tDU`K``c%OG%YPPN$9r(qRQT0UBqtB#Hgn) zj@39Vk0m`_&scisu{?OR{wS$?ok>Tw+4Eux)lkU%{Jd)Pu%;ruD|fYOGE0*po&ESe z^jz1LkXBWLRuC5BY?db1o zrsk9hCHJOE0%qS1UK8nEk2kM4WL7OU?Yn68ERUsr`?I3jR5=hiIZ*q>lKkhb)tXC} zmY(lWH99MDR=svuGu=X-c@Q)6e(a;7r#anC9U1k!!n3C^_MNAD+wlCWk>Q1j$|nKr zkE4D4i5`=;fBp1jS*cv=>S}gPZqaBoHX4Ji{IF1OZZO}8<)8}z0Bn>fJ$--xruNpB zL<9h%Nk$}6!2vc3#bzH6NHa49C>kDPY>dG>#-h&rB4G<)M+P+lR8Gj%jK5}vzYTq`s(FOQSDL?qZ5+U<|HfZ6n*|B;Jiqqm*Yp8aSvsRkY% zc}+U75s5b9iNvodq+;r)IrO|B8gZfB-f2Bo7)i$Kq8H5~W+Z4ZB(8J#WGBO<898kL z)*Y9<2`uq476O0BT?s`ZaTdf$&2grq*n$+F^ehg?O3)VrutZMER$f6e1Ou0CAbN(! z$+m$2DtFCIeSOG}6H$k~VhBT)`FqP+Tlt10X1F+d)(603hYYoeb#RG1o4BPevC6u$ z^_0uO#W??%=s<|OA`!=_IvHfi4r=hoS}&DYu8jrlrY31%h2@Xc!F#z4dX%2kzUkJL z%>s-LJ&u#Leh*gfyYNYlVAc5e72^%z)!2SW2XBi@^^YiQ%P!Dc5nhM0(a}Y@;fSR( zqc8e(%-r1K{T24y_aELq(qK`4_k;b~k2X3W0KB%JvShD>l|I~rFw$}4xKzeV_u`4I z_JkclQ|u6_l~Wixm%$4~#3cxY^phRB_$q~Qj7%v)LPb1kbq#Wh%;WQJG>J%F2%pi0 zkUJ4u9!W%IgzRP^&i#)VQB6%2B!V*n%#m2<5dwdB7^VEX@MTe{+_i+PUr7Am zS=nX3`A%e#dMcpxje2R{MsISCdG2!puyMl|EueyEHy1%ek$X1 z#y?)PymDZwR*bIcH!?YxO+_W8F4BL+z!><(&05j{>EB>#DI_YM+TMxo7D#DPBoU?i z7)@8#Yv3u#0!Uf(COb9V&nFDjRGeMGH%Gr&>;rvWKn{gyE z?=%|CI=C5}bmel)8gYFhe*hq!q+G&lUWp^3&qFpsDPirlh`RIPcBeQmMc76vUSPX!lr;JprxdAytTxo@a{s3so*F~L?k9&wF#d4;ke zV$$a3%Vsu`W*y!tIJXkw-_Nx(DUT@Al^x2(rMeCFbWa}88upF>|;PK7u)bq zXgJ=Q89egCe>_@V`c`UCI^Wi-1N5{ua9SNllW9!t0;w<}|N2oT3o-IJ65;NCT#lUc z%W;MfC=5A*h&JVgiHpfm5S@OET*8Qx3%O*9h~Z6_)42?SbDo@wv=`*jnas{SCPeH* mFpdMyo)5iv`D$1(GLR%cp|IROieSITZd15fx(%cN_rCxoNOo-i literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/38.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/38.gif new file mode 100644 index 0000000000000000000000000000000000000000..8b1c88a3e8658cde7b25b6e063d23d010f7ed86a GIT binary patch literal 3615 zcmeHIc~ld39-o=q5R!1nAxDM?f(RjrT#0}Q2{9;W1Xl3ang9_}4T&Mv=qe;!f?|nR zi+8-Swp;6Jz1lSiqLiwQXzQ(@Xwia#r2;hxJ3+DAee1q`uYc@6J2UUi%x}Ku@ALV7 zW@?%+fi3k0ynz87P&M7p-d3J?AIn+ z>TyrbxZA#)FnHd}b|u>W(>nV@wWIg2{Ri%ajee)AiT3NMw#(yxJVmhGTlDd~G4_Y= z5BHyP{A_;NmS+1l@b)J-OU)QZ&;A_?Ny9^J*AK^BXb8W5)XV;Tyko#*d$RTF{)il= z`-@AHtviAo&vu@ujdk=Mt;=$CJlopZ;M()G|Mi2uJxxK6kNey{Q`G%!`q0x)zN(Kp zQ61iSlKy!mudgltrIM8&!g3GsE`hxY)Zk z8H>Z7T$npN*g9})?)n^(Y1{0cmZWbE$sI%Y`mITQch|jaBDd`#{n{E(HU;W?uz#T0 zy~85Ca#xzP4>$E%C9mmO;k<$3j8ne+T|&dFVJ+vEC^yOsStfi=#7RV&meNkuZzFWJNQOD=vx| z!-SMH-EY6CJ&5*)saFNpz*UFq+9`vz;+mtSH1H zs$_YwMyib}E*Uo(K_Dy1SIbozxw05`MwI3$muiw|NTsh^P^jL9EiR#tv_sEVDx$Sg zRWy^%aJDpRn33_HO%;l_)+L%u*>Cm!+rTB+%T==IOj(I?sXAYVn2&R&QgQfdnN*`x zXDgM9M|V+Nq|_)&ij*ptpA`p(XGrtq#m#wR zpTdqwWyPej83HCVm6^hh6$<0nf>?G8Q^?@QjQR?c`AZeDV$G;;!Ee5?@AP$&L7_r2 z3uJ2f2eJa8TB(3Xn&!ygT?^}-@!t3ryt|f3@AQpEiivhY`%h4hE+PDJKE5R`^75AY zvSNhqYJ}L}u)}V9_3O~!z{_9ye}2)|`}|o?_fJosbp81FQRl-49qsq;-Mw@B*3BE& zfB62|)wV0v?=D}u_-*SqEf>x=pKG$1O=ti9^;c(3pE`Ns%j3uXcJzxQpa1om48{W zT)XUprJ54;5|y%8v3Sw@@}k0pvV#0Psbs->f1W>Y?ws75*?-E;%A6&hIU^%|`m{9B zRAH)spTgr#Nlr?f%wZ?Q$Hm4>Vlf%f^r*;*5fj40#)pQCqXom%puhnCv3|Zj-ebHx zJ=`f|k{i+0g@DIlF=!No$V2cDnot1ffJmoiIe(%6ei#4=0eISKEDj&r2{4aHn?gwj zajw4k+?sfg0|}BYin!K8uuzmssj!S8#aIGz4XG9ULf+%q`g2pSb7?79okFtGb1QR9*h8c^l8IGmiuNVyF8j;l5v zXX-)M@fmlp33o$GxG*uH*Lg0BJCBmgh2WLt;yv+sHmM7P;`TB<(bNTQn0slDODoe zmZWkkKJyVi3oIL7LuqMht@aMJ`dXp=)5se=#-k}*V~R(RE)(6@TUgsb(j2>Hr0}7u zr^+I9B2ZKbV$1M&yw$AJV{k-G!}NA?1(2_38RX49@8#Dx3)gPLp(+S*KrF!ZHw8k+ z6|TnHTlG|wTS1wx!`wuD&^&qIrKgmyaxHTa^fnnGEV1n9g3sG0r|74cXC>Bm{cO}x zpMF14HPADHnHad$VVq8V(e~X>0}i;!?mrpd$){2(+ne>cHUqD0po(Eac@s6|q+qW2 zT$K-9wSXE719~dwm{knL69Ev7-`AM8Mj|kzs7Rrvs#=$8MSG!njk+dGAVFgxClKL% zhKNR>62s`fVI_pmH<&j1%!l`E$ev9Qah(`p{t!kYCq`yztw-_-d;+DZ!fQfKf)(${ zwXFRJwYw`4ECg%G)N-phvuXwobbSx+7jDsQrm~7k^(
  • ap0JjxBHsOrGeE$JcnI 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/4.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/4.gif new file mode 100644 index 0000000000000000000000000000000000000000..d52200c51ee924f0b91132e5cd3737fadcf40ef8 GIT binary patch literal 5689 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 E2fD;K%>V!Z literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/42.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/42.gif new file mode 100644 index 0000000000000000000000000000000000000000..0eb1434b4e81d4437098efe06cb4e1ab44a86211 GIT binary patch literal 5305 zcmeH~Ygkj~0mo0^kOXo&KnQ_Au3TbK#w}bT)dT{>Ajri^K(Gmy8kCK&isJR;LP$U^ zkqSk0YrG-a+6lUjwbVgCMJo$M$H`}j>#dkk(v3$GV{6*}BFogLpYc0=u zKR>~-Jn6LD+if0BwA`ZyF>EB(yK615x?nd;V zkF(F}WncgC7d>^znVFf&rS|QWQ8Qz^F6|@k@jx{eP(~{%ERT7eL*BjtR{s)r*-F7F|Ic4F_%#>Q{@-nzWU<6+;@FF#!;2?VbE)@$TkUDu(F{Rab{ zj>xqV_J=Cp+vnDtP{#JY{`!;Ax~JbheE7xI8*MRH58y{m2Yl5|ynp}x-^TY0o{Y7O z@9L}%KkQ4|>W6s#dHUGze0%DNmS_z<3TiIsq`S*!pdSL;?WRoB~#w?QxscUMI-QzHu z9RfFt7+c({YgNI?1e!k?L%@)Wi~Y9-|K_~_X=(u>Fwl!okSfiLI;q|k#b6fXMPxfP zWJlzIZvIi*)S+(cq(Ugdb9;klgiuHX@S!SSF~^MzqM#h=W@^q~z*dhkUw9+OgYF|l zIPaA}t+)ArkBpaJ*8a`aY9@;pDSq6|cL+%OV6|KxQ;CLH8}y+j3Ao&wqLWFx(l7^b zTw*OGHLcQnvRA5jgK3vVNL2ozGS^-|G%>1RdgzeyZW_SSbJUk z$B+c*`WQ{YtbMo?Ej#6G+>fiF zQ;MG5NExrIbjNwF3^L}Pt0P@?SS)QTh?#Qt1NZSDuzM0)jI5Y8*!MmCc6h1zE&wRY zpP`}YlvRH^dzkmX!17J|9@ctZ@Na^XK`FG;Mr$WTT>pYrZsN`eltY7)r(I(1OIjfU zVPwSih6qnc5VAn)mvSg4+)67v$MnnM1zbT0s)XDM#-gzVJ3X*x^kVb!(5h-+)Ra`S z81<{5NWYfz6cjyvh`qk68x3=fLE?+`2rqAJf-dN>zYs_CFE}%D(WeJZELII2s6Ve& z24K&ylRkb@g`gF-pg*}OI$WuxsqC7e8)W&N5YuCC z#<-Ez`fb`JjnO-Te5gsr*Ptkjj>1h_y+0{sITre1O-z%v1Oa|)C{r5VG2lbiRhwK# z`Q_ns#nA9rXDSLn`{)uBT?xEHc^HXSJn4nYkQoasGLXv((^cs~US0(6jGXYmhVYyWZ>ks7XK-x`2II^ZVXSyC zBEIvzqw%ne#>6ujDOaiyZ`*&?^a~*7iR?Lq^G4Nv`}{6;Byclf2ubG?#IYF7t*qrF300 zAW&(@H;!-P^HG9<09?GWo4=+uIVztMe&uTU^++%IKY=p8|fRqn49 zmG`|BdlYkxv)FQ^aVnvz{PLEL>eRmzdGk6 zNVYb~_q{JtiwFpzjP9{&V`vpjOZfla(xZ(XpOgf7bio~uqS|J)(Qa;O5=Ojt#Ftu^}7U&!WricKDW0ccoQSQQT;IoEgc;-VL~uu zB2OCf!=|uBQ-s<8_D!HOeLZqi8XYV~lqbw^iYkT+q}J~HE%3VjdZO(C7wKd zj&>nYT(nOxv(Dc=P#~x9l~o=uIj5)#OK(Y1 zb{|}=4ZwzM$d?~gqwrMW%IBvG22~Eus-hvXk8#C{f5?AkBHxgXQ9zuy9TSgHRkc*% zBl>`W&$>>h=%4=62(nAKUhOGAbt8mv- %FKWjtVdL1P%I)BVn;-eLqw9kid`E}a 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 Sh@V0@dM8BG^xew?0{#K$QUnA5 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/44.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/44.gif new file mode 100644 index 0000000000000000000000000000000000000000..ad44497691d150ea5d02a900231e48ff519d9616 GIT binary patch literal 4126 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/45.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/45.gif new file mode 100644 index 0000000000000000000000000000000000000000..6837fcaf214a394d8efb68f5d48c9ec6f9ea1fa3 GIT binary patch literal 3417 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 Be_#Lr literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/47.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/47.gif new file mode 100644 index 0000000000000000000000000000000000000000..58a083611d7c1afd95d136e3a7fa798c86d0c2ef GIT binary patch literal 2333 zcmeH|YfMuI7>2)d+R_WymLAH@(krc}g@UaIXysZc6v|DCFw{lML_tx9cp184*`ZL1 z8relh&=?dGG3o?08na|UL^p_06A@IL<1U+y4ZKZ27)xB1?B{;%hyT9c&-c9VYf4Gd z>GQq88%FK$%pZF7=(9wa`2rr;b`38_u1Ni#YoJ?-PQ<{Q1Mt)dw;~qzlI6dI!E6&; zR4S&=!}MYJULot>=bAnPrxl7WG3{Y~-ZMS?R7Fmx;JV!ZrqlUU4ZS8>Z=`Gh;F>w* zWhsn$!lPWuh!*)(43jHi(gHKv;NC*y$D;Kw)BTS}1Ph!lJ4eqYW z{g%g>PJ`Dq@YVr0ZM$bS!gWJbUl8S%5Sg+-e-LyD{YIE@A&eMd!^;HdTx;!1^10x_ zc$`oDtrVHP22a+a6LRQY#Ggupb_~Y&@Tv?ZN#um07ao2`LhsAe+>VK!o12r&xo0FU z&rUEbOVoxerXTCRc;d(gnBusp#@^!YJb!2 zEjv?U4%vf|HLNuvF}0Gq+TAmiuS;9EgNm>?9F_(VG~2`EI{r7h$iiJ^X3FI&h0z%4 zL6hltEXxP6ib9p1!Xj|?EmBaZ#MNqCo0Ah8sZwch=Bm7K5>qgR3O1d7XW6dOArs4w zeTi$Z#&Hh_i;mYzcq1Kd6%cVmEO9zGLXsFOmM(H|d63aejf+oa1m%s#@d~JB5|f|p zkhEvl9ORZ>`lH{%a6?t;-40NlJkhCOSOwY!SO8(U=fBerAgKw)j~WY_$x-8%lobMT zW7F7yst~s7Xu+w;z)Ft$W}2Y_4NP#ueD06(iH`OLp_|x4(cOjy1niZi9*xp*|H8+7 zhYy*DhrPq+6`BzeOuFpBOEN^>#J7Aurs3C{ZP@)Gp7QeZ;u1*0eVkjWV_tTGL5lLsZ%QmVr#~iiiTQ&bn%& zAUN6hdnd%DpD^<88eN#Qi{-iWM!ilP=s2j$lAgWD8yu=<+sn3hwix!OON`05TM$!!`a0qYQ+D0Gm zjNQ!D;g+hwM9lVZCZ`~Ozsn=@Xkban)%L+;Zpe$0I!N|e`gP{GrlKDW&>WlF*Kz-^fm&{KNpQTa;MH3VbMTSO)Iy`7$cc!}$F;)1yn)K77`oAz1q&dbPwMcqZS4-Gw#0WxD0%0Pa38b|9mayJf#kALK}E#J$BBuS_BN>|>~!Pl rL2H#EiEFFJCp(ZsP5Wj&2CVH=rzDGc@3T0Qspp*aTJ3^`42b*_XAT7W literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/48.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/48.gif new file mode 100644 index 0000000000000000000000000000000000000000..7ffd1613b5fa36c3ffa7daca94e7e8cd6f8d82ef GIT binary patch literal 2689 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/5.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/5.gif new file mode 100644 index 0000000000000000000000000000000000000000..4e8b09f15101f3e3fad0777ad068fb559ed483a9 GIT binary patch literal 4567 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/51.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/51.gif new file mode 100644 index 0000000000000000000000000000000000000000..ad3f4d3a8158106dbfa48e521084e6c56fc6406f GIT binary patch literal 2785 zcmbW2cT|&C8^D8Xg%%Z&T1NE>ggZfd){;2d!PHv`+M$v4?8>B!m$Pb z1HiljQ0u~&NiBVwRV>VHnMf%TrPhsS{3s2GE`SpRl|ACSU7}kZqMIEjRSg9BtuyKM z6YRRGgV_12h2krjLlJjYyn+-zkF0QqM2!P;bx&rCJ4IQIqSV^?h@0GV@e7Pp2_sRQ zRWH3(B8e;#v$93ud9u)K*$-Kg(9|VT%+fj5GX64SpPkP}6Jc$uq@rChCQ$V8UWea) zbT(?HpnN*Fq`~vja9)MDs8i0HS$;Ok#j?bAx}P#q#CN&!*ORlUWsm*X^9|4C!&8gh zgUf^C^1*j2FULeT+ZWn?mDD_u6#pcx>0j)ArQpph_5QKk|7PhaPuBE&>GAMF=b(sP zU3ZMBcq}3)AgS(?R`r!RQbwciEOK8h_6$v=l#a41 z#o3L5J1t8Cyk=L@0yR;PTQUD|pbmY0Hs}7k=**eS#@VcN*qLbv9I5`S^8W@wd~l0On%Hh=oU{25Y{ zD_Q<(;^B8;;<6rb`OoEU44Io3|61-s5PP0=L+0Tv2}%&=G)tpypVb9Q$yaAWGiI;c zkhi=Tz047@Ys4|P=9^!ve9si6{V2YYEqnA*-u4U8&~!ZQfjGBWlzjiq5BY-f?m14Y zm|f$oYb1%}h@%UI5Bda`GJUsxDa?7ez)a&um&lr5$ig@yAvuC_F7HwVku1f+FWSzeYvzz8v9AS2|D7#6JUN6e17v{Ap6bd8723L%y2g<<>12qS$ zssI20rRb!K8xW19W0l_n_Dl*EcacB``Vh_&{p~=rjV&M`5pM@NVeJBTp`Zx9M5hoc z!6U@g6Blw32gif#4+0M`5lj+=M4)4VOp>2J4Z*YnVew=i0zx@n6v9gB2Af`Yl7y`8bD_@uy#0p^zLqKhSA0Ci08Wn#I z;f{9v5KB3;1NqYF6oiEZgTXLoSelcm=Pe*`IDFLy6l$i_Frx+g)3Ho5f12@{2Q-0( zqY^1}BH16f>JjTh4y4oC@`k~3VKoY^9?u@nr zDOb$#L_7j)X{+2t8!NC46b!S4Ky1-SBns*1XbnbTY;0}NYd$_^3rC}E;0|as#un@d zfnXrE)-Y=%)CP<}B5iG~EFo*Q&i*ty)*nY$i%V3-{a}mwS6c*%O2E>|R8KP5Z>Vh{oENF|ei?{^qM{744>s2mD)K%pJL z7$^z_f!n}9A8hge&#qZ0ld)KBkN;|yHIcFhSBHN!zjE@|@gevt&kR+0I8+q!mF13iJ!bI(uQ z?yk;`_D5~4Ef1TU8XM{#{8(38Q(aYAaliatS!qe}-8;97ZWZ1v;M~a1%gxEo%FIYl zOJ%1dCna7_h>yD#8xtKBc{L(DjCJMmrO+Qjf|-nYKq32+{4QP~`kp^Wz~g+d z-eu+i*b#$9Asr6cAGAC06#{N+1GBcWghId; z=4Pf}?%%ifi#@wdj6p`gT|0Md|9snLhFcA`=x^S%QSZ}FbaghY*VfY1P*+o3r@Uf} z7Sy!$0SbVr^0ra_h7AA!Ed@ZKz1^vT(-vlaK_wu-jkDpXiXliX{Niz7ton|HHsgyU zhqgt60O8;8Fl}3{O)!Rmu^LDRuc})PMEBvi)A6Bm&Yjvk`)!PaX%khktj|xYp;zFT zBU^T7sK>>&;m0yI8=mmC&BA{XzwkrmQ-XFo-b!%oIHEQc3=L$%u2!U!8=Y1+xBD}EV? zfCL3o1cJ66%}=)nqcbyY2iktWe&t)o{!%xi$g3nRy}SWy&0HD$(&>sdOw;rOHtse!C*dM+u;6h`P91FBq z-Y=NgIgGCJM#nQZ=iKx%Oo?Bo=*!+17@rMw4=9l(cI6!_YVZRMcNwT0zv7zgj)^Vm z^zi{_E%4R4wivqZ`AxepIvo?XZhz%IsEKBLH9SiHHy)<-gj?R-1F_T&Zlale{PtMk z)oZ10+9OR^kcl&UpBueqK!tFGZGz17c8ILl60v*RE9X-uqMUYs411ijFz1|2!0X7q zb8ioMMZ7fC*>R-*O`s}CV|k!{?!KlGVWsL+%tqdyu(~x!L+@cF!5qG~)Hhm#Wz@Ah zx0;dAu7q_Y-odeKU=7xGcyKe5zh4PU^ZaD(;qY~z=@e?M(;fytR$1WtT-Q`^1$}Co z+z6!2hP`u6roMZ_&|BtMNjlootFR_Kk9TVP4NFF8ZBw;0`5TsR^8dh+r5(~|Rz>Lo Y?y9hKVE|Dl+bifDKf^#>4e-@}0Qq7Ywg3PC literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/52.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/52.gif new file mode 100644 index 0000000000000000000000000000000000000000..39f8a22846945358446ed010e2b9bd50dfcc7a25 GIT binary patch literal 777 zcmV+k1NQt!Nk%w1VHN-u0Oo%H_RysE&7t_xrK?&j)|+hq+_3!Ft?IFT)0AV!c0$aC zRqD8h_|TsJ>&E)ltKOw@>9vIN%AU!BNyvIc?7@=%;kM9?T>taZ_{^H%oM+RGRsZhF z`r5JEo^R2GQ{9+i_}Q%gdgjaz^&bp0Z&w@W+~| zS1ZnjO~rUYx^+wGxrybje(A1!?Z1)y)1|L4Q~-nP||Tg`+@%Y8|vQYP4yVcL>g zvt~NObU**!w6tnI+^U8B*sH*IM&_z|#eGZp)T8>;r>$c?$Bk&qfJV7zHKbH2=(mZ+ zgI36gSAsHn*{k%yj=+0P^2nRuqi?otLDrynz;{Fc`{2HH zLjBdIzja5VP9(HtHTc)8{no1Ese0nAeZ78I+ml?_ms$MWwEzGAA^8LW004ggEC2ui z02Tli000O7fPaF6gnvseg^7xP76J~4i-a$bjEyHm@r9@V1n%lQKHaE#3h9gAw5QHw^!C@58A-z#?=7-ZCbD#S>_nhy%@3*|?JLj$r zU5vC!qK3OKOyIjL&=f_HLf;KT*4p zQZHIL*>mDv-L`YvVqf%rGdps+K56x(eBtbHo$;}uzd}0JS}|NHxm~Bf|9!#3hMa)| zDaI$A1BX-3rLP^W*F9<~ynSN(aJBl!)R389s$bvw?#gaq-51dF?#kK0W4(oOk6Vjd zl)UkdlJ?B7ryW&o`i)(Mab3EYQ(Gg(4dpMdA02BcGLHN()!#I5aO>55Th64cdC-(| zwOC--@u~4nqj9|JV(vQQqt;8=(I;hVCN5SESIegd&yKZyJy4;Xxn48%Yi)PVrrDum zwVPMYj&|Ov(T&s>bmqnO6vK~ON+vEGtV;-P&x!A?*#7eJ{->>_51Vs-Z`$|ZOwr`k zQzuiRjZ=f?cYHSW^ZuKa>Y;{;!RoB3YgL!?HW>SkyuMb`y;nRv)bQ$7qwz+=!{*|X zim<)|Dg70x&o7rxc9a*g78|FA2CFj9?@E{%ta~+Ff2TU_Zf%CqXmm4LNAO~zIqXOt znLsk12LJ$KS|1(>as_Gu^7RkYC- zP{Pp}5i!DyWFb?83tR>IYgihoLJF$|phhZ@saTo-oIoUxhgrzjWG3RkHxPAl0FG^1 z08KGDawQB>34Xpp5(I%X8UdnEX*9?OB$FUAk%YW7Ux>n@(pY3V`0l|W(UhVstSB!3 zT`Xi4fJ;=X6)Yk#EiH|Z=0}h#6NnI#$u#*OlYJ3{uPR-p7HE8BD*T)WF02wN#R|1p zE(1*-1@ZC}bpQ^@^lb@JMR@po$1)Y+O+f^qTuRgk6hw$XB1)yEy568w>L~a>X?zf^ zib+?%#3)!LPf-eC1dg90Bij9WAQKSDhP75HMm9wt;mU<6Qdp)A-l=L?+7Ba#f;S0dk^gpjWs+ zD3+NF-lh$DTQ3(@ic?_`Un!S@Zxm*UKac^%j|uTeTr!FOzbb#r75zV|5s_qwruO)+ zc9}CFJ!l%fZ+>L*{`kN$I5A-hEE2{GHgEc3W88+%KU=@WKC$uYCez431$Zct_tv9$zn3-G%Cf943UTgU!N7ry}dk_dAQ?oZlLSO zE`RyRd8yM9$Hfkd?Coq9E|_m)ZH2{HqEQy+X2=!eHf@1*0JMM)a@!!k0Y?C^G-hda zIt)gi?Vp>kx0Ypw>SM!KI2GiF%B@^;w6Q!MswCgqRYTBm(HyWU*Wbpa`euwQ`( z?ld}ph9XzSBFsm1-tb@LJnV#;e?cw?yRuZ+b(fAQtFN7aB`_E zNo`e`D+i->_KEXu((}=12xIHxlF_CMVxWVt_~h+fx(#$2+s{26mvxzO&0IWFah{&+ zHZ~Ycu#012!K9HY_Ixv&V3Zp+BG}70G0sXSEHbzCdVyK*#_hCN*q4 S8f7`(nZXDyt@tenSosGvnoLjt literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/54.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/54.gif new file mode 100644 index 0000000000000000000000000000000000000000..e289d929b9552beb7be70329b7cbefb1d0618008 GIT binary patch literal 2196 zcmbW2X;@QN8i2zdHUU9)$4j6T6|#|pkg|m=0&O4yDyY*Ek}CwrViIH#8X#;!fP&G% z;({n0L5fgC!J;fx7EuSJ6skCc5iO!DMeEQ(dwT=o^oP?QbD#S>_nhy1>wCU)?zUhi z*_&gHutq$QBVOLB89$TJt%#ne&wlbv@m#0sX>;C<%y8&wJ2chSpS2xY=pHNs@2L{U zzf2e|2=B-Ug`Qu7rmsG0DeB4I(UlVN=EvrdNtcv+MjPfjj@?zpydF6ZJ!*j_uRN|hID75rRAc^~Lwn{g zW4=?uQ?iy3^hE$T7wu+`$(t{dFyU>5G{}eRaK6)a#zgz&_Klf^|wk3%H z&0HOBIJ`7;daF>sZ5x-zo0fVGFF$U1Gu=AVrh0z$2sD4~a)OV#K1W@jyZBwz z-Lm+*M}&*lmC*QwpW4d1vUbi~DTmaT=Gx1kpKnamruP=@8z_ib8me0!se>ji&30Fg zoe*`)7_)6(K(ig==j2ljiifoahK@-F%E0cd2nd3lAmdO*WCV@MX5i6S9W4X`0gIwo zY=F*@ap13yf2x?nO$22?EEvZZ`l9B)?LYy1o-b+_k%eW6X<$4*C|v?Zq=!aw(-XPg zJe0p5;FC&86^I3(j02T32z@IoX&h33!eF+;$<>1 z1%pXRNkON0qD7K849?rzTjK+d_kcA#q-jDKC)GnJbzbv82c=vIUo7K`gn-5)Csvd! z^F_g#u9hGWvsmvO3#I6lg3w%%0F%lQV{m9JMj+7CwW2MRg@gZ1lKn6Y#&mc2AaX1Dp(3{9)lDz3eZvu{qr4iO_ zgN0HVN5}=&;_~6RZ*6gZYD=L>K#oi#i4=*F);fS4FOrF*@ggxmiy#4REDo11)Ce|f zHfXh8Iw;{E0C`M_NC2!5rtsgB0fFF+V_@lcEc1U=zG}<+f2d*LWH6fc_(Qv_iQpd8 z4Bs_BJb8C~Kp}i)B=F(Tf_{7Rdig)UF8%w}FE3v#{=6_hH~akA%+u+go;;p<^shdJ$t6+boHr|CyrNrd93npM~|ouSCoHIR$8JgE;>|Lke`>U$jQ!< zf1a6fFg-0bE+QwwiX@S%Le?MQJk0{<`FA|a9iN|3vXb<S)7PjMJ~WhE@nU!X3VC;NO5X0$~7U$#WEPGlS39 z&90mn}`MmRFg6x+I1XVw*+WNk9Lpx7AVlVoRR zbl_uKZEe*S8+U9{^bUPHJ6~TLa|I(L+0-GKhP1$7kZ3BEKoAbewLt`SI}(WroEI3Q zo$Y_w5SintW9>snreds-dlwkSI?lK0XZs3zx=T0GT$-wEwTkTQcV=W&It3Z6w~dP` zoT66TEY*Esb*=%Rv5mBy8mBAyUEL)P^dDMdnw)wqwD`LHh-|LX&=L{rR$^hGpJiz2 z_j-XH$}HWa|I!vacypk@EJbX-fUODC!zvP{rqxr(LWCAUKi7hm6Og6P3vF+v1_?^7 zw}z|6I;+EU%uPes|4rGyebcC2z;H-TQS)rxf zYik)q%Pq8U)-^#`x@Ig*P99e}`hRa8Q1ex>%_cvw8c=iSc}6k%BZGrohK^QRp4uiz z=X~94glXULAg*F?x2eZYmpXBPmLA}i71`NzR&?5atL_KU%{2`5t*G7P>yO>c5g+{y DWiE05 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/55.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/55.gif new file mode 100644 index 0000000000000000000000000000000000000000..4351083ac8e0e65a64ff362cbd480f2c86eab4fb GIT binary patch literal 1971 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 O9M literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/57.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/57.gif new file mode 100644 index 0000000000000000000000000000000000000000..0bf130f0d930a1fb72ca83265bb504304cb08090 GIT binary patch literal 2705 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/6.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/6.gif new file mode 100644 index 0000000000000000000000000000000000000000..f7715bf52817bc577c0d33cd0cbd17da898848a1 GIT binary patch literal 2213 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/61.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/61.gif new file mode 100644 index 0000000000000000000000000000000000000000..f092d7e351cb195eaf707dbec74d32ced42f432f GIT binary patch literal 2495 zcmZvddpwls0>@{J%gYR7j4+up;~ps-g^~<|amh}yl}_7Y+vT7Wvfa*3b5mn-Xw(QEPFk@m^%H;^Pm=R@3$tBn3-FJp|Pv^7m^UwSD_xb+5kGGGPlXIvZOb_;w z4hu>WRu9bO^{zf1oIGFNTQT^7Th-I``h94IsJwfkq%pUy5n8e4q73P$oDwNA;h+TRVnUA`_FJACO6c)M++_wCByGB~^fmi{J_E`ZV%ux@x^UqeupTcd6~3D0Ton_H_0dMVc-Ceds;7_7Op3fM?JF4-(bWew!@<4uipnOUf!5Fgk22^bcK<9t@@i zGUzOJ!o^FRL@r%h`{;L9cxhLT+S;;hHGHjda`bQ9$hGqE`;ukr>qwwg1iB)Ba1E>= zIQo{*H$+bQXPwkpP7f2#7?1(FWFz!_ND+*?I0CmU!Y%?{_}D zgB9YeWcn`vZu)Kp4uqR3Ift;0s3_^+y^owVpxk|gF^N>x?KB?ea#FcF4ONmD~V zOSFv{O=Ol(xcPUxJUjIaHyf_N$Ui51Du&lY#~(285j^#0 z+p)y$5wMc&90YA8OG+(0$;KU54((qFY0GR#-E>RB_@<5BWT5+m-*`@lH`jD>3>hhz zO~73TLx7mVd9In)PVrDkF?$jV7g=gyLt50aTFVsNwqKcC>~5umE*o)@KCH9I?^-I1 zneX=3MJU0PVLC93;v#%wA)ts0iChFHIM&yQOGI(gh+HEG+aj=;={M8#GNt0Ik;)Iwz)ZYnw*K#u=Ajy&HMWM(0U$r z8WcwtvX<6=aIbLZ?X}1*j+C92WHcJL^@OhOiBq^avgOX`vt{m75!@Q6lt`x;`D%um z?Jn0NMjJ)kEy>0Fh*CkaD%CyCN!$K7PU~y;^Q(&xo3w3TlqNDEHdnE60n`H)%qPm< zipFzoO~l+H3u8)cb!gM39T?sXK*x=f9A)oD8L(0jeU7z{R|c?*%fVLe>6YAlQ!cOi zD<#NKxt}PxpPyiX%)1Dg$8`<72${F8-8KH2c1C;@iq9t!`EpKj;qLyZ5UzYpw)%!O z*%YSPZ0>k$J(D9)C5odx@fG2tVqgz{w$-kxU{cj& zQ`HSX+q^LqYmT2w6K16$uolXvd78?9fsqdv4jq^pfTBc1g@vvj=GIqKdz z>}2-8ad;CGJZ?`4i9pK^M8;a*G;+zN5lBHrIj+bG*TAx%sI?v6t5pDvGO9(PYHNwa z+K)gxyA(j%)_@KTe*$#s%{tI@xp^zAa-P0oCN(TEbFaCMl|SI##?#UGbFB8zSkvTV zjm{^`NJxel)l|G4qskPctFUi2#n9TR60Z&Clq&4QMUFn{ETAVB8$^~0ZmLoXA^Ubj zLiQEzqAf%A-9GlPRI$uTx_Mb%Q!(?Is=w-kvOI#N_TQnIhg!$-^3&(&W4wIWDJ*=n zD>5SH_I#u^`C373Y>nE|@)|V_fY&IcAkhG}#wm=P!cdCyN*22z zDC{HyrbKL8n1Bb&E>X^f6c)Fp6N*t=3SeBV&1Ae2C8p%%Rb_kbbSG1!s$q=Y{1YZr zBE}%dY#mbQiz^%}>TvCk?!9erOiccHgSq00G{hJ8OKsB&D?W|QM@i-wbUNU@UEhku z(%0Ya4RFKvAzk(Msa{jvr{{{?7f$y@X3%k4b#LjO!qIY(zCJ~CV>M)K&2~J2p6kP9 zd8t(**0*4*=}hVpw~ zk>4iKC;9E}`7^(Cw7Eno`6R#7|DRt=YQ#S*Joc_vr63V;(D;a;NlNliR7Kb&hNt4? zZ&a6}DrHbrO4^{Rl(}$cpsH*=IpmK)Kvi+D0P^xn16W1s6NxyeDm*OpGcqqGyy?>J tNMrII%;*&@oa8uz=YCKFxNClV+=*Obvw`TNkx8belN0|*V!~j%{{ZDj7zF?T literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/62.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/62.gif new file mode 100644 index 0000000000000000000000000000000000000000..7fe49840bf69219fa40585741f901fa0b187c044 GIT binary patch literal 2017 zcmb7E2~<-@6b%XriWPA~E&7S5U?CeMKrqS@kf@<3s92X0ldnl3i^)eK*p}kPCV~ix zfJKFXfD#rh3MxeF%Bm~@$`%k=RFE|W0{x-ja#}p6bLO0xdGqdl@7;N4Hc}idt!Rr7 zix6#oh~B}G&K^Ze*N58H!N&G6xk4GARi+#rdr?2s-mN$Zt8|D2&!)bX9 z@fj60tpjNVjWOA^RZV?0ErU@hrAfEy&WTE&RlgllC~rT09U&^daj!XAEKSO4Z0OE?(wdM}k@xJCRMvO;(u1r*Sz~*@ zq`Y0$DZiRi^Q5v@Qr1~s|L**?@+fgtLCM?PXKk_BwY9CiaoN&K;!4HHXkmFrbBA21 z9KBQYI=!&*?6v1`Er`#C!PsnP2RB!HTNei$3Nu{|zJg8eR2P6ugJ|%&ZiA3VqkDo7 zupd0g;*gNtdH0b3i$Ox}#8WX;o;}E9Ir;KIS6^p0y00hQih}inbI*>EI=Tju;zFI0c!%_ zFjyQK15bh})|`kZ5OEg3#6ZH)_>2R@ZDhxZSn!F2WI_;+h(`PP_@I2uP+a~&G}g+> zO2vW0nZh2Xf+HM=CN$*;R!%aIK>?l5;z2Af2T(E6_H(@<5)#hzpAy)-kF*>CYP=j2 zoy$fGX*@I*g;A9>=}4u1-IdM$=q-S@fuHjIPhx@F5gv%%1`4>|d^!lbuT+KN5$*XP z4dU|MxLmJE6^PHtOFLFmUsf*4rhUJu(PwYFgL?aV#!>(Hyh+YlUT+lEN)7ysu|ckI5HXJ zvkrp{M?RMgj2BI0O_>YMaZ0`kEMw|iFjHdDa58As(|-2UlP0)-RNIe@3m-nVKFERl zoewv*+SsUaWLWV*J~TMc-`D%Tr~BR8H(i|_?XTNjwYJEbn;IMH|EjC4kyck#R=j*s zURGLCT=e|e($a^f-#b&cY~Dn1+UV#&wzspj*|46p z&YEatX+gl7o8hn+G|JTEyS2tfYgVr^T!}OQRxH>5cG=P;dW#n=TrgjEp3dBF=4j8J zrKLG@hKBm|Y4G=8@IhT`0m2VqqWVNspEzRXh`(RJvEwIBo(c>K4hiuvHMQOtdR#}q zFwx(=H_}f>UlZqsTW+Eky2fa^dRm$zKn^rB_L`5MHH*J~PM-F`%<2aO_;S@38RMvJ~yY(nXQh9H$#C+X_N6J7*$<3$?X(0mkzJForwl@3YZR zD(7ypEO`2UwUg#jKaV}(l2@CjA#_$wOK5tyC3Nl~OJ>c>w!&a-TDZC%g`ap*7qR1L z3eZjo)z+#wr(=^XLRhaYF-#~^4@uA#A)`uxOotZCF_r@COs07jYnjoR41IHx^3z(23>`O(`)4x0N+N9Ug7`A4=;!*$FD3vz)I&(EtPkek$P3WH+wR>*i|S;@u67A}zhS~gjRlsT&q z>2ep_vPiupx+%s62_mZ6VYfJn&1J1cwzHwYdCiDZL26Es7v)%gW`st7wp5Hko~cZ8 fzaAzQ7Zd=~XY>c^Z;pPWn-TRQb#LAp9fbAYHD_Zj literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/63.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/63.gif new file mode 100644 index 0000000000000000000000000000000000000000..cf8e23e5b2e83bf4f383cd994510a7a04a74f57c GIT binary patch literal 5871 zcmaJ^dpwhS{C_sXFl^<1>C9Ym-^C@j)tI|sE=eUB<{mRs%jz6+EnQ4Sq#PpA<%G(S z(wS?b8&OELLLnit5$U(p>D2kXe&_f6v*&sCxxK%i_viinJ}z#~R@M|HKnV~CfEj}E zoGWJ5!G!N`*`uT0x@OJb;NS;G)9W%a)3%0-+pXt4a0^3&D-j!4a6gF>u{J4 zfv)_icQZcrpQ8yYU%u4pYQDEI{IJva^ZSWUVViD7N58VvU+!+5CD}g3V^$aDpFDXo z5 z>dNw6tnPB#b$-ysRX%@tsP~P%=}PCV&+`+@k2+`EY?j~jPPv)AaT<>mRru%%d!(+HU0?VI@{sdI5V2e*2xumZa`nlGL?{CQ$*u=LD3I}?7<;Za-T zrPbB9ciNXTBf9+ES3k|qd2LuJKlJf6?`_tv{Er_O@()gIc3U`8(4P?TX+NbkI{aft z^J4uUqtz8(R+g3`9N#^Buvl3<9=_$VlXd&{fbqlW{BY7-cl&%^*2;za#S?we=EEuYKd-Dr4LeeP}ZZJrNt`o;79 z<42cI|Hj`Rxpej7{Mg8|(5?K;2#*gJJx8MHMQawC8T<+}D854PRv(M@bm&>4E*}7#m*5gyg z_SJXi+ah)@o;WBdI$F1VOO?2IpPJgVjp3IsUx5Fm@&^Vll(845XZ#;v-G>K2r{vN@R zniNKlrZJ)u;*p|=l+c7^1|B7>^c#d-w12|JCz*bQV;YvQ3(cg^&{$K92-0^$PtX6~ zbl0wbtdkhN)F1W!pMjJ7_tL0nUusf9GChncG}jiTqS+AXR0<=3?w^nl_k9(~Q3;HM zq^JZMl1N1AZ;6f%PuP=W@YUSY)5a}6i9v}Eqq;fbQNjSG(b3^H4)%^%OM7dKlNH7a zi*+)$cP0`Y%^fyiFc=3*jHUH=U&n;77(caeD(#-wqcGIn!jW-(ptiOKkYF%y3l|L?Dx_F_w>U`z7it@8% zrN5V)IbD3JsPJS#{)ywq@;JFij~qUfbMQcRR%S+e8aws3U-$20rR-(y*`3Ttq9@W4 z;&;Wx#zaR&Mo`1ULMb6TgLedN-?lYyOTcD-KVKiRx0ff$!`;o*#o5V`=wMISv=MJ- zYh!J-!P3Iq42Q*_O-+oA3=Q*?xfqqLB}tkcv`S5s9{R#H@0y9NQ5my?CbNJ~K_ zCBz|OV4*f>EkVQ~fB-NS35sY31)yJm87v0DE@@7O$~$A4c_mz^zMOFlhQQ8{ki%JW zN<+9{IfPmz0mLH!IZEooG+J1giwwxBqCq*7co$HX;4%{tVY$i}i6AHv*@lNM08nU6 zs*_U*US~yv8w&D)y1b*nL*~*RE8`Uo+;0gT2Y~`*`aBvH)*nLeyhc@>4Udul0n)>M z+tKV)m^XN34VHo^FfIfSi*pF*otFWehLv_^t|>`I$=HkRJ(GSt)@6)-0(86-CyD!5 z+su5Q&idUxH%3d0#KD1s9u0(wF`Ax2_dQkbZx+u&p0*v}^3E5kCOhr7~@0}s!>8j=ye2AMco`~|Dna0bd?;uOTijPl`)H>N5Q5Zy(*DWvpxC<>bs}x};KNwZ@0PKL4^W7{GCNxv>4>tio z&Z8uFtK0JbF z?Ti6i_fkp~RPDJLOR?TLE^)<&SX&crjI{ulDm9uKs{8i0Os_2<32FmxfJe5 zoc2kX&l)oFW+4+1m_8tV&8bDFOnBA04!{!tPo}=Ftb3R=1P{cQZA8d=EmBavX3(Ztt)vi zAc)HG13WB`f<`bhI-{GjALJV`^afb^4jJnMB3Abqp3u2_iSpaDIHffr54w5pHaXSM zb55x&HGK`-v9~$riDY?ATLl8Q$%a^Q58kT#SY68@G^jSkJ4eapksMIOd+hFkG+s~Q zfPkEWr18-+#lRS)K5An`f35E89Wd~u-azk?IFu8a4EKaeqJtZx4sg5(>FI%zPF4zC zXG+IkoqDWbIBIo2#q%f&1p2v$-@$bQGvYNsx=$`kj^J7A=zhN`8zjMfUGcW$7lVMQ zK{1@v*>Y<;dU8(#wZMc$U)NIi6zq;t^|g`eo|EXg!qVIpUAv(TR;+ctDtQSqdanuM z0FUkoJ+!@=dcp2JyKRh%S0*bNOhVcfb={7&mDQyrWOu0T+P!ZnH2eYFAbwr1uY#*b zF_|25AmwpqaBa3?@Mm_ffv9!2&(yh$2jel9&G|McqKrI(|0a&UFY;cu*X9i9LE<*o z{A?TZmBq5MlF5FxZ7*e@KjRb4p@^eBKh2@EMk%317H_u*D+<-`CWI4USa-|tG=z~I zO$tJA%4)K9J4`AByPQOr`9m5JSP-;8$?lCxokC^zaTXAjQG|4GRzU}c3r{^%HW&F< z%dqX^%>D|GNC$jk%9Tz$;%Ldo=y|6$lv!jHblgz4ClBV`v9-Ck-SxUTFnz;SJ600d zz@OTGLu%ADo3$|MmWP0l3c36WE9o>)qNJ*`L;o{GN1%#ByqxwQPub3JWvAU0iD9iy zV?77mZdPT3h@tF%ck#R0bo?}lhPNbR6g4dH3NRHW1%O#xteA}|Fm@|A2nK)jX?1(5 z#9Dicggud&$rbaSA|SBrTxE2_F<1kVO!(a(*pW#Qie;U#G8hU$66ESl>6JWGa+{q(|Ii}%9Y|WIH*Q`w~quVtxKcx(VdQ7B^(!Gpv@IOViIt2 zH;t|3nwFv&7ormA9g>Y&c-^ssi3UBt$rKOhyh#>+$u?S*hW$h}$XA^ZQLRW+sVj$< zmL{g|N(dx-9@7zoa6<~)IYrhuf~QWQJu*Kg{E~@Q66A3+3xF_!XpSA%)YwyGDI}&D zujbMV5o&-^jkl#-A0yA1a|RchRs;%|ECJXmIq+LPoz8}e z!TcYId7M-iq#zXr-mW{WY?nowOcR%>W~(X%HiTDXHXS$FHp?-qAr-l!wvh-#=L|fe zU4s4!710gX&e=U?Zw7)oU|YiD%05JGuTaF9?n0VJHXLO`$9LCK%+8i5CHr`#SOUB4CnT@~7EQNj+?sJXAwc zYDO~=Qjvu>iV$s2&LKsX7BMSZ)8MiehJv75)(X||bGjZeoEvwX2e?*=<`&W?&MRpi zOP!{`3GW6w36;3LF%0xLe(T->tL*`kvdU`pm!f(TuPB~k{o(JV<`?c)s@6N*^9V%? z)JaSmf?s_K?QaU%>wYlDOz+847K*fgZNr_%`L==xiY^pRW|V$e9-RiLHW9& zieP;k>8m1GZ|}U|rV9eOOHoXHHZgVB6s@7+p zw;nI29`#!baTuBF=rX{D)*rkZh5-Rs*$GGk9GQBtP_U*n$Aa>ZeO^+J1EO7dQ7(ld zgWxsE;9{0j9B`^Mj)YgX-+e8LftE$BGuf9o!KqX{EA{8}{xw+kz~L09lIvV7=QDN>`gq-7EAEI>~f=SECp&7yDkc#5dO`yZ#ZuK(;k5-j7+@SX<;F&Ba|ji*SVnZ zsoElXu7bWHm}*j+-CA;2N$uINZB6!rYL*pk)Xk!~1jXJ$pE{U%{s}Cpda4KUwLMHu zP!XcGGoIJsM_OWIpQ}U0pP2jg z1fQW(9}bqW9Qcky;?wS@UGFq56(*vcA81&IwxyD^=J2w|!e|4fY~mYfDDlubSIGYc zB==Qo?|vJdheEy`FRYb>JZM@6&9wsij+7{xH5lLZ(XJsCt68iXBciT|tHtth{QCn;bYK(hGG9@wFKa%+hAP7{KAa5^OM{9VYLxOaPwBX&a8t3f zgj5;hIu)Fmi-f+4u_^4{Y@WLX?nwd1I?b(&05VqA$irTCM%wy=krjzLo&Yu|6to;Z zlFEe$dZBfG&LoT(*L@HUC=wA4{Q7IECpuq1{L*cpZ9ru&$K(;Ttai)60Ld!Bq~f4f rEJ-o=%(~<%mp~*1w)xk-i@w=4VXc`Kqy4WMXl?8h22a(6C~W@+b1jPt literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/64.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/64.gif new file mode 100644 index 0000000000000000000000000000000000000000..a7797198af0f3bb98361b1b8737784d0a8c2cba0 GIT binary patch literal 6448 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 E1xN?}xc~qF literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/66.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/66.gif new file mode 100644 index 0000000000000000000000000000000000000000..bb6d077504e3b39871f72150c1f5fa43d4e18bfe GIT binary patch literal 3029 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 n_4QGfNp-c(#;#v)!?fJ=R?7`D2^Hm literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/67.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/67.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e33f7c4f762b5c4eda2a630793d681345423734 GIT binary patch literal 2701 zcmeHHX;@Qd7QP`wAR!P4LKMNv4ag!{fe<2l0wNMfff>RmQ$ung5we&B2sE}NQQ0hl z0##@QM5-uS7zc}jV`Pm>RVw1%Vylj=ZLzi0Qai)kFhb|YIKSsdzvp|N`+eu0_dVx) zXIZ;0ksTp&2i$>2dSD!leuTm7o^ZHfYuo>&Z`mPt!x{U-cRY5C*l#mC41LSmbHT;( zUGkI2nA2#Rrb`|(j*iO1cHQ?pn>;WFZ!LY-f^QrJJ1>S+WVvi^!dIWSuQ{>0b;9GY z9lH1^R@7iOPITRW)o1$v;bH{m?OsCtkax=nP1QzdzT*05X7UsP*FHsTpY$DAMmjku zHJ&8a40_cKyS{&MMd$Uv!e*DAJDlB@v7OgebU^t17Vx9nkfdcv>K1JA{$-LT+^>20 z*PhFU9}1p=KA*p=ujus4GGL#0dDncr?3a|ZhSRZ=1VYCp|7s4ldSK}bF7J8PhFLnj z>w4I+JCvP6u7>XATl;W>Unkd$;tqWpd@ms2%JYp!ZuyKpf*;VCr@mm{{^?CcEAB@( zx33b`9=e<|HY094!#O-AZXH=K-Q{|9M)S;X)z$<44FiFZSoG=!L84kW$5MoU&$>XH+5?|RuZc&g_t5tDm zw33n%YDoxHsm`Y{A|fKJ984w!(V%Eb6l7NT zNFj#T@uDDvgDR6rIAIZS^zd+cNJ1Es%U~oh;#mwPJ3cOs7akWE9~Kw7faNL0MRHi7 zUBF6y!-l>VYb^%33bD+C)v{t(lBib7!TF#$ve)~s zFNl!(u^tx-7r88!KCD3MU5ykrYVM!^`1R#4vp>Ih{?m`oe)#_B%y-{@^W^KV9{>H( z!v|k}aew;r&+gs5bNklK8&lUOuYEdk^~&W>E?vBE-ZDNmIx;+T&V2T7X9fpOpX&ek zWZz#;{H3?&qvOZAj~?kdeCWf@gB=Ij+gkU3@P144zP;}?H8$+oU0=7W_T8N|)m4=h zJGO5#84cy{yuDTbR#~a8q_{|{QEyQx74pJ@%`$0z9xM^(iZ;FZ=bVij{**1u`r{um zGt%D>q^0sx)+et^TAP@_xD9-fyB-6$pG36_y?M{(1_q6hr8^^LS-#_kcH}58IGV?6=MdMD+5tUHk&0(>?g&0>`2iA*g?VMdC zmdaRm(jLeZy-5U`hE1NFpy2)?1KNnSCyiL>(s4(Os~bURs4yDQHnyRo9cFXYNIGc+ z2pWb^k*-f4kC~d!HQq2r>t@r}blF69IS`U4(--l}tGJdA<(@ATpV^Rn%cCu8r)H*y zXJ*^F24{JO`Q$h_FC=Fvu=G`Oaw*$$jg{LdK^2uoN{&7$7w~EV$Z4s1O07Phh&(;P zi7c;3?jY#3wHQ5$fI{nY1UVqkhjNOl%+DnD^*I6KUdCi}PyuZ8SChsXNkzrQMJbbP z&$HsoB}DcV&l6UA7AQy$)S2Q?E>}s-%cII9FJ&8%K{F=ojVJH2ccCY(D4jSsf~(-W zdI!dCx2=jSjZe8Bl8{_!$~YioOMS3AJ)9Y-*=>w5H{8ngl|A$y4q_@wuWq_~{zQIS zs9x9Ieyv05ygZZ{kkf2Pb@4V<6V}B)v}v(%;|n6T7^|ynU?vzJvZJP|MYuUd2HD`6 zOi+Hm&_WL2x-moYw(a$Kc+P|g7G~-Mh7)u*?!>io2`ZH#lbLZlN1QCU3@6uz?6Qi{de e&Ns&5+yCAe|96dnSxgQJK<1box?FDn*8B_Be&pBy literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/68.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/68.gif new file mode 100644 index 0000000000000000000000000000000000000000..1a6c400d2aee3c2bf384ea98105ea143a7be97a9 GIT binary patch literal 1424 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 ig6vPE;(vp8vN=19VTuwMWT7@8^o literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/7.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/7.gif new file mode 100644 index 0000000000000000000000000000000000000000..e6d4db80572a8321fa233d7efd82636fd896bf49 GIT binary patch literal 3398 zcmd^>X;@Qd7RPTELI_!bkd;Wn76gSL8VCjmtHxL~xKN`N+n}hegVj>h8D8>cxj_Xi zSjVMQtw_~*7^&k@lqw)n5L%Qf;?}a%$_S(4Sc^ztZUR`_vClK#=R=++pYA>9{D0^C zZu0D;h{zS5h$r$BKyDljeeg~Gzpv>>pPpV9$sVltd~rxR`uyh5zR4p4e|vFC+O#|H zv~`i~-j?S%p|-YZRTls5(~6<90^7Bykw*t_{yoLk9^2PA*Vd`G-JE0VS^KObdni9- zrJysV)|MRV zaBJYG?OEYU%}|A8=;|Avr3>mxX52f;8N5DkxF+IZrSRUFsV^@0bbS{2%grTUf2o$GyCOZ|TCDz;sYyO{G90Ih5#fO`;Ud>i6G9Sc>o%d*M-cshOA3A78FdM{l3e3 z=iv1o5zA&zFEoa5a1lOUE(8}Z0nH~Xq%>s4f(3602*DW{)Gyc(>Ix$sVT<^*41ecb ze}Y$%isPfk8w2pXi+Yb!A2;jyVLW~SpYku+tU=qPli8pW=;!x4wjSv$pR&(F+B z_YEXRt-{9|pf4I)HIeQ!AzOyucb@(bM=` z(PEI~p50sx_;3Nc9 z5xKbS5K0$YDtkL?YQ-f1a9u?2idnO)BD(NSXl0^lvMR{K`%0sstSMecA>VpCmWdB^ z@(>jtRYRiLFa{*W4kQjCV$D&p_HwZHqNP}SnKaZqq4a2a~w^|4;5dy4mPab+0 z@VU$0XQ*Ye@MVyO=Y)qZjsZ(tUFW#U_<&P=UIrJ=CC(>K`nsukk{Tw!D5MIx2HKYc zBoPM^@!4k+E#||T@_s1pJ1iuZQyxDx{C3t%$E@qIVmLUg7y%7+kPk8Nv2wxU0{nWd0|dEHQp6|+bed(ZJD^M+%YPXeg< z6m|}mqJlK4fJ}`HEkT=%@he;Igy!|N?h2UV^j_`sm712_GyXK<@;ive8awt1M6WUg zUttK?8A2_YhYx{qhAP?cw+z`4I>+BPh7d+j(>cJu#l0HCdkIiacX&I7&!dHl0sG-& zcVOoSC*Xn9XHL@Yp4v%)%FVsZx5?Y+bfE|TY}bM0V=;8$(mRb;uk6Bmr5gIVty( z*3rs1?|2L_`#PWd<0PV`j&%l^G`RF|{_l-Q8s7*$=lf0> zkGpU#m=7kwKH|ekY95k6Bt{`{aA0Vi1JVHSj2pOC0oIHR4z%rp`lwn0V+d)tfSZtNXQOIQR zHj`(XLTM6{ghJBRI5GJ!nW9xHNg7j1%Arc&2DBuK6Ds+~ISQ&PHt8;SYBft&kjUgWbx@~;SRvvy;0@rWbt?pvmb>Pe*9vuRaX&t0 zpsy_?GY`Z8li6Yl&o-@Aps9d^x|`BSg`mhHHY??HI$5!LwOF}-zmP;(A^coUP7^CC z=zxBl0!sHE*NI6dPYcOaCZ$lR)#3Xyby_@m+qPkM0*+oFwZGrfJm%=S6h3H2pyl(Iu2|m8IIiYIUBrX7AZE zQl{fthK+q4Onm%wVLeU=8N4)dA=~m>BSSoI--MB&!^qnlLWT|@e>5)S^S=`^_|?dr z_VW*ZRdP3`WZ-bJC78J}iSdy%zMZRZM<^b?EMkV;0463JGl1C78#6$y%Gq5-Z~1Y> zlg?IxSn*`{`mBpJyApMu4sSXUhl2uoM90$bmD{~p^3kWU-ihR^iLgxn=0E@wiTV!_ Ch@v0> literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/70.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/70.gif new file mode 100644 index 0000000000000000000000000000000000000000..416c5c14a19fbdaf8fb8d6baf5cf7e31e54f751a GIT binary patch literal 4590 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/71.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/71.gif new file mode 100644 index 0000000000000000000000000000000000000000..c17d60cbd15e80d83629b2220b562a224eec5554 GIT binary patch literal 5304 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/8.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/8.gif new file mode 100644 index 0000000000000000000000000000000000000000..66f967b48da173b68cbfc92986e4426ea35ade74 GIT binary patch literal 4050 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 JEmCgizW{zshWG#g literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/images/face/9.gif b/agent-analyer/src/main/resources/static/js/layui/images/face/9.gif new file mode 100644 index 0000000000000000000000000000000000000000..60447400d31b035f56554b29af1aefc76bda1698 GIT binary patch literal 4221 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 literal 0 HcmV?d00001 diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/carousel.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/carousel.js new file mode 100644 index 0000000..b8870ac --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/carousel.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
      ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
    "].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a/g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
    1. '+o.replace(/[\r\t\n]+/g,"
    2. ")+"
    "),c.find(">.layui-code-h3")[0]||c.prepend('

    '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

    ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/element.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/element.js new file mode 100644 index 0000000..ff46106 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/element.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
  • "+(i.title||"unnaming")+"
  • ";return s[0]?s.before(r):n.append(r),o.append('
    '+(i.content||"")+"
    "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/flow.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/flow.js new file mode 100644 index 0000000..1a6be51 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/flow.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/form.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/form.js new file mode 100644 index 0000000..0c7b014 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/form.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var t=this;return i.extend(!0,t.config,e),t},u.prototype.verify=function(e){var t=this;return i.extend(!0,t.config.verify,e),t},u.prototype.on=function(e,i){return layui.onevent.call(this,l,e,i)},u.prototype.val=function(e,t){var a=i(r+'[lay-filter="'+e+'"]');a.each(function(e,a){var n=i(this);layui.each(t,function(e,i){var t,a=n.find('[name="'+e+'"]');a[0]&&(t=a[0].type,"checkbox"===t?a[0].checked=i:"radio"===t?a.each(function(){this.value===i&&(this.checked=!0)}):a.val(i))})}),f.render(null,e)},u.prototype.render=function(e,t){var n=this,u=i(r+function(){return t?'[lay-filter="'+t+'"]':""}()),d={select:function(){var e,t="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=u.find("select"),v=function(t,l){i(t.target).parent().hasClass(n)&&!l||(i("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},y=function(t,u,f){var y,p=i(this),m=t.find("."+n),k=m.find("input"),g=t.find("dl"),x=g.children("dd"),b=this.selectedIndex;if(!u){var C=function(){var e=t.offset().top+t.outerHeight()+5-h.scrollTop(),i=g.outerHeight();b=p[0].selectedIndex,t.addClass(a+"ed"),x.removeClass(o),y=null,x.eq(b).addClass(s).siblings().removeClass(s),e+i>h.height()&&e>=i&&t.addClass(a+"up")},w=function(e){t.removeClass(a+"ed "+a+"up"),k.blur(),y=null,e||$(k.val(),function(e){e&&(d=g.find("."+s).html(),k&&k.val(d))})};m.on("click",function(e){t.hasClass(a+"ed")?w():(v(e,!0),C()),g.find("."+r).remove()}),m.find(".layui-edge").on("click",function(){k.focus()}),k.on("keyup",function(e){var i=e.keyCode;9===i&&C()}).on("keydown",function(e){var i=e.keyCode;9===i&&w();var t=function(i,a){var n,l;if(e.preventDefault(),a=function(){return a&&a[0]?a:y&&y[0]?y:x.eq(b)}(),l=a[i](),n=a[i]("dd"),l[0]){if(y=a[i](),!n[0]||n.hasClass(c))return t(i,y);n.addClass(s).siblings().removeClass(s);var r=g.children("dd.layui-this"),o=r.position().top,u=g.height(),d=r.height();o>u&&g.scrollTop(o+g.scrollTop()-u+d-5),o<0&&g.scrollTop(o+g.scrollTop())}};38===i&&t("prev"),40===i&&t("next"),13===i&&(e.preventDefault(),g.children("dd."+s).trigger("click"))});var $=function(e,t,a){var n=0;layui.each(x,function(){var t=i(this),l=t.text(),r=l.indexOf(e)===-1;(""===e||"blur"===a?e!==l:r)&&n++,"keyup"===a&&t[r?"addClass":"removeClass"](o)});var l=n===x.length;return t(l),l},T=function(e){var i=this.value,t=e.keyCode;return 9!==t&&13!==t&&37!==t&&38!==t&&39!==t&&40!==t&&($(i,function(e){e?g.find("."+r)[0]||g.append('

    无匹配项

    '):g.find("."+r).remove()},"keyup"),void(""===i&&g.find("."+r).remove()))};f&&k.on("keyup",T).on("blur",function(t){var a=p[0].selectedIndex;e=k,d=i(p[0].options[a]).html(),setTimeout(function(){$(k.val(),function(e){d||k.val("")},"blur")},200)}),x.on("click",function(){var e=i(this),a=e.attr("lay-value"),n=p.attr("lay-filter");return!e.hasClass(c)&&(e.hasClass("layui-select-tips")?k.val(""):(k.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),p.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:p[0],value:a,othis:t}),w(!0),!1)}),t.find("dl>dt").on("click",function(e){return!1}),i(document).off("click",v).on("click",v)}};f.each(function(e,l){var r=i(this),o=r.next("."+a),u=this.disabled,d=l.value,f=i(l.options[l.selectedIndex]),v=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var h="string"==typeof r.attr("lay-search"),p=v?v.value?t:v.innerHTML||t:t,m=i(['
    ','
    ','','
    ','
    ',function(e){var i=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?i.push("
    "+a.label+"
    "):i.push('
    '+a.innerHTML+"
    "):i.push('
    '+(a.innerHTML||t)+"
    ")}),0===i.length&&i.push('
    没有选项
    '),i.join("")}(r.find("*"))+"
    ","
    "].join(""));o[0]&&o.remove(),r.after(m),y.call(this,m,u,h)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},t=u.find("input[type=checkbox]"),a=function(e,t){var a=i(this);e.on("click",function(){var i=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(t[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(t[1]).find("em").text(n[0])),layui.event.call(a[0],l,t[2]+"("+i+")",{elem:a[0],value:a[0].value,othis:e}))})};t.each(function(t,n){var l=i(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=i(['
    ",function(){var e=n.title.replace(/\s/g,""),i={checkbox:[e?""+n.title+"":"",''].join(""),_switch:""+((n.checked?s[0]:s[1])||"")+""};return i[r]||i.checkbox}(),"
    "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",t=["",""],a=u.find("input[type=radio]"),n=function(a){var n=i(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=i(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(t[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(t[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=i(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var u=i(['
    ',''+t[l.checked?0:1]+"","
    "+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"
    ","
    "].join(""));r.after(u),n.call(this,u)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,i){i()}),n};var d=function(){var e=i(this),a=f.config.verify,s=null,o="layui-form-danger",c={},u=e.parents(r),d=u.find("*[lay-verify]"),v=e.parents("form")[0],h=u.find("input,select,textarea"),y=e.attr("lay-filter");if(layui.each(d,function(e,l){var r=i(this),c=r.attr("lay-verify").split("|"),u=r.attr("lay-verType"),d=r.val();if(r.removeClass(o),layui.each(c,function(e,i){var c,f="",v="function"==typeof a[i];if(a[i]){var c=v?f=a[i](d,l):!a[i][0].test(d);if(f=f||a[i][1],c)return"tips"===u?t.tips(f,function(){return"string"==typeof r.attr("lay-ignore")||"select"!==l.tagName.toLowerCase()&&!/^checkbox|radio$/.test(l.type)?r:r.next()}(),{tips:1}):"alert"===u?t.alert(f,{title:"提示",shadeClose:!0}):t.msg(f,{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}}),s)return s}),s)return!1;var p={};return layui.each(h,function(e,i){if(i.name=(i.name||"").replace(/^\s*|\s*&/,""),i.name){if(/^.*\[\]$/.test(i.name)){var t=i.name.match(/^(.*)\[\]$/g)[0];p[t]=0|p[t],i.name=i.name.replace(/^(.*)\[\]$/,"$1["+p[t]++ +"]")}/^checkbox|radio$/.test(i.type)&&!i.checked||(c[i.name]=i.value)}}),layui.event.call(this,l,"submit("+y+")",{elem:this,form:v,field:c})},f=new u,v=i(document),h=i(window);f.render(),v.on("reset",r,function(){var e=i(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),v.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/jquery.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/jquery.js new file mode 100644 index 0000000..fdaf188 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/jquery.js @@ -0,0 +1,5 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=!!e&&"length"in e&&e.length,n=pe.type(e);return"function"!==n&&!pe.isWindow(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(!fe.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ge,"ms-").replace(me,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var r,i=0;if(n(e))for(r=e.length;iT.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[P]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function f(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;i-1&&(r[l]=!(a[l]=f))}}else x=m(x===a?x.splice(h,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s1&&h(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(r,a,s,u,l){var c,f,d,p=0,h="0",g=r&&[],y=[],v=A,x=r||o&&T.find.TAG("*",l),b=W+=null==v?1:Math.random()||.1,w=x.length;for(l&&(A=a===H||a||l);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===H||(L(c),s=!_);d=e[f++];)if(d(c,a||H,s)){u.push(c);break}l&&(W=b)}i&&((c=!d&&c)&&p--,r&&g.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(g,y,a,s);if(r){if(p>0)for(;h--;)g[h]||y[h]||(y[h]=G.call(u));y=m(y)}Q.apply(u,y),l&&!r&&y.length>0&&p+n.length>1&&t.uniqueSort(u)}return l&&(W=b,A=v),g};return i?r(a):a}var b,w,T,C,E,N,k,S,A,D,j,L,H,q,_,F,M,O,R,P="sizzle"+1*new Date,B=e.document,W=0,I=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(j=!0),0},V=1<<31,Y={}.hasOwnProperty,J=[],G=J.pop,K=J.push,Q=J.push,Z=J.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,xe=/'|\\/g,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Te=function(){L()};try{Q.apply(J=Z.call(B.childNodes),B.childNodes),J[B.childNodes.length].nodeType}catch(Ce){Q={apply:J.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:B;return r!==H&&9===r.nodeType&&r.documentElement?(H=r,q=H.documentElement,_=!E(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=me.test(H.getElementsByClassName),w.getById=i(function(e){return q.appendChild(e).id=P,!H.getElementsByName||!H.getElementsByName(P).length}),w.getById?(T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&_){var n=t.getElementById(e);return n?[n]:[]}},T.filter.ID=function(e){var t=e.replace(be,we);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(be,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&_)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=me.test(H.querySelectorAll))&&(i(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+P+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+P+"+*").length||F.push(".#.+[+~]")}),i(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=me.test(O=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=me.test(q.compareDocumentPosition),R=t||me.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===B&&R(B,e)?-1:t===H||t.ownerDocument===B&&R(B,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===H?-1:t===H?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===B?-1:u[r]===B?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&_&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!_):void 0;return void 0!==r?r:w.attributes||!_?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!w.detectDuplicates,D=!w.sortStable&&e.slice(0),e.sort(U),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},C=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,we),e[3]=(e[3]||e[4]||e[5]||"").replace(be,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s,x=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(d=m,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}), +l=c[e]||[],p=l[0]===W&&l[1],x=p&&l[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[W,p,x];break}}else if(v&&(d=t,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),l=c[e]||[],p=l[0]===W&&l[1],x=p),x===!1)for(;(d=++p&&d&&d[g]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==y:1!==d.nodeType)||!++x||(v&&(f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),c[e]=[W,x]),d!==t)););return x-=i,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[P]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=ee(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(se,"$1"));return i[P]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,we),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,we).toLowerCase(),function(t){var n;do if(n=_?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&_&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(be,we),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((u=T.find[s])&&(r=u(a.matches[0].replace(be,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Q.apply(n,r),n;break}}return(l||k(e,f))(r,t,!_,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=P.split("").sort(U).join("")===P,w.detectDuplicates=!!j,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var xe=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&pe(e).is(n))break;r.push(e)}return r},be=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Te=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Ce=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?pe.find.matchesSelector(r,e)?[r]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Ee,Ne=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ee,"string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ne.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:re,!0)),Te.test(r[1])&&pe.isPlainObject(t))for(r in t)pe.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}if(i=re.getElementById(r[2]),i&&i.parentNode){if(i.id!==r[2])return Ee.find(e);this.length=1,this[0]=i}return this.context=re,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Ee=pe(re);var Se=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),r=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return xe(e,"parentNode")},parentsUntil:function(e,t,n){return xe(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return xe(e,"nextSibling")},prevAll:function(e){return xe(e,"previousSibling")},nextUntil:function(e,t,n){return xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return xe(e,"previousSibling",n)},siblings:function(e){return be((e.parentNode||{}).firstChild,e)},children:function(e){return be(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,r){var i=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=pe.filter(r,i)),this.length>1&&(Ae[e]||(i=pe.uniqueSort(i)),Se.test(e)&&(i=i.reverse())),this.pushStack(i)}});var De=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,r,i,a=[],s=[],u=-1,l=function(){for(i=e.once,r=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),n<=u&&u--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=!0,n||c.disable(),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,r):r}},i={};return r.pipe=r.then,pe.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=ie.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,u=1===s?e:pe.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?ie.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);i0||(je.resolveWith(re,[pe]),pe.fn.triggerHandler&&(pe(re).triggerHandler("ready"),pe(re).off("ready"))))}}),pe.ready.promise=function(t){if(!je)if(je=pe.Deferred(),"complete"===re.readyState||"loading"!==re.readyState&&!re.documentElement.doScroll)e.setTimeout(pe.ready);else if(re.addEventListener)re.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{re.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&re.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!pe.isReady){try{n.doScroll("left")}catch(t){return e.setTimeout(i,50)}a(),pe.ready()}}()}return je.promise(t)},pe.ready.promise();var Le;for(Le in pe(fe))break;fe.ownFirst="0"===Le,fe.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,r;n=re.getElementsByTagName("body")[0],n&&n.style&&(t=re.createElement("div"),r=re.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",fe.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=re.createElement("div");fe.deleteExpando=!0;try{delete e.test}catch(t){fe.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,_e=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!l(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return f(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return f(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=pe.camelCase(r.slice(5)),u(o,r,i[r])));pe._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?u(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=pe._data(e,t),n&&(!r||pe.isArray(n)?r=pe._data(e,t,pe.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),r=n.length,i=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
  • a",fe.leadingWhitespace=3===e.firstChild.nodeType,fe.tbody=!e.getElementsByTagName("tbody").length,fe.htmlSerialize=!!e.getElementsByTagName("link").length,fe.html5Clone="<:nav>"!==re.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),fe.appendChecked=n.checked,e.innerHTML="",fe.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=re.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),fe.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,fe.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:fe.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:pe.makeArray(n,[t]),l=pe.event.special[p]||{},i||!l.trigger||l.trigger.apply(r,n)!==!1)){if(!i&&!l.noBubble&&!pe.isWindow(r)){for(u=l.delegateType||p,Ke.test(u+p)||(s=s.parentNode);s;s=s.parentNode)d.push(s),c=s;c===(r.ownerDocument||re)&&d.push(c.defaultView||c.parentWindow||e)}for(f=0;(s=d[f++])&&!t.isPropagationStopped();)t.type=f>1?u:l.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!l._default||l._default.apply(d.pop(),n)===!1)&&He(r)&&a&&r[p]&&!pe.isWindow(r)){c=r[a],c&&(r[a]=null),pe.event.triggered=p;try{r[p]()}catch(g){}pe.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,r,i,o,a=[],s=ie.call(arguments),u=(pe._data(this,"events")||{})[e.type]||[],l=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,u),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,r=((pe.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,u=e.target;if(s&&u.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(r=[],n=0;n-1:pe.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&a.push({elem:u,handlers:r})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(re),st=at.appendChild(re.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var r,i,o,a,s,u=pe.contains(e.ownerDocument,e);if(fe.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(fe.noCloneEvent&&fe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(r=h(o),s=h(e),a=0;null!=(i=s[a]);++a)r[a]&&k(i,r[a]);if(t)if(n)for(s=s||h(e),r=r||h(o),a=0;null!=(i=s[a]);a++)N(i,r[a]);else N(e,o);return r=h(o,"script"),r.length>0&&g(r,!u&&h(e,"script")),r=s=i=null,o},cleanData:function(e,t){for(var n,r,i,o,a=0,s=pe.expando,u=pe.cache,l=fe.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(i=n[s],o=i&&u[i])){if(o.events)for(r in o.events)c[r]?pe.event.remove(n,r):pe.removeEvent(n,r,o.handle);u[i]&&(delete u[i],l||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(i))}}}),pe.fn.extend({domManip:S,detach:function(e){return A(this,e,!0)},remove:function(e){return A(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||re).createTextNode(e))},null,e,arguments.length)},append:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.appendChild(e)}})},prepend:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(fe.htmlSerialize||!et.test(e))&&(fe.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=l.getElementsByTagName("td"),t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),f.removeChild(u)}var n,r,i,o,a,s,u=re.createElement("div"),l=re.createElement("div");l.style&&(l.style.cssText="float:left;opacity:.5",fe.opacity="0.5"===l.style.opacity,fe.cssFloat=!!l.style.cssFloat,l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",fe.clearCloneStyle="content-box"===l.style.backgroundClip,u=re.createElement("div"),u.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",l.innerHTML="",u.appendChild(l),fe.boxSizing=""===l.style.boxSizing||""===l.style.MozBoxSizing||""===l.style.WebkitBoxSizing,pe.extend(fe,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),i},pixelMarginRight:function(){return null==n&&t(),r},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,gt,mt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!fe.pixelMarginRight()&&ft.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),ft.test(a)&&!mt.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,xt=/^(none|table(?!-c[ea]).+)/,bt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Tt={letterSpacing:"0",fontWeight:"400"},Ct=["Webkit","O","Moz","ms"],Et=re.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=gt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":fe.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=pe.camelCase(t),u=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];if(o=typeof n,"string"===o&&(i=Me.exec(n))&&i[1]&&(n=d(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(pe.cssNumber[s]?"":"px")),fe.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{u[t]=n}catch(l){}}},css:function(e,t,n,r){var i,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=gt(e,t,r)),"normal"===o&&t in Tt&&(o=Tt[t]),""===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,r){if(n)return xt.test(pe.css(e,"display"))&&0===e.offsetWidth?dt(e,wt,function(){return M(e,t,r)}):M(e,t,r)},set:function(e,n,r){var i=r&&ht(e);return _(e,n,r?F(e,t,r,fe.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,i),i):0)}}}),fe.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=yt.test(o)?o.replace(yt,i):o+" "+i)}}),pe.cssHooks.marginRight=L(fe.reliableMarginRight,function(e,t){if(t)return dt(e,{display:"inline-block"},gt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(fe.reliableMarginLeft,function(e,t){if(t)return(parseFloat(gt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-dt(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Oe[r]+t]=o[r]||o[r-2]||o[0];return i}},ct.test(e)||(pe.cssHooks[e+t].set=_)}),pe.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(pe.isArray(t)){for(r=ht(e),i=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Re(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=O.prototype.init,pe.fx.step={};var Nt,kt,St=/^(?:toggle|show|hide)$/,At=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(De);for(var n,r=0,i=e.length;r
    a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",fe.getSetAttribute="t"!==n.className,fe.style=/top/.test(e.getAttribute("style")),fe.hrefNormalized="/a"===e.getAttribute("href"),fe.checkOn=!!t.value,fe.optSelected=i.selected,fe.enctype=!!re.createElement("form").enctype,r.disabled=!0,fe.optDisabled=!i.disabled,t=re.createElement("input"),t.setAttribute("value",""),fe.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),fe.radioValue="t"===t.value}();var Dt=/\r/g,jt=/[\x20\t\r\n\f]+/g;pe.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=pe.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,pe(this).val()):e,null==i?i="":"number"==typeof i?i+="":pe.isArray(i)&&(i=pe.map(i,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=pe.valHooks[i.type]||pe.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(Dt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e)).replace(jt," ")}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,a=o?null:[],s=o?i+1:r.length,u=i<0?s:o?i:0;u-1)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},fe.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Lt,Ht,qt=pe.expr.attrHandle,_t=/^(?:checked|selected)$/i,Ft=fe.getSetAttribute,Mt=fe.input;pe.fn.extend({attr:function(e,t){return Pe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),i=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Ht:Lt)),void 0!==n?null===n?void pe.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=pe.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!fe.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(De);if(o&&1===e.nodeType)for(;n=o[i++];)r=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Mt&&Ft||!_t.test(n)?e[r]=!1:e[pe.camelCase("default-"+n)]=e[r]=!1:pe.attr(e,n,""),e.removeAttribute(Ft?n:r)}}),Ht={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Mt&&Ft||!_t.test(n)?e.setAttribute(!Ft&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=qt[t]||pe.find.attr;Mt&&Ft||!_t.test(t)?qt[t]=function(e,t,r){var i,o;return r||(o=qt[t],qt[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,qt[t]=o),i}:qt[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Mt&&Ft||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Lt&&Lt.set(e,t,n)}}),Ft||(Lt={set:function(e,t,n){var r=e.getAttributeNode(n);if(r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n))return t}},qt.id=qt.name=qt.coords=function(e,t,n){var r;if(!n)return(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Lt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Lt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),fe.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Ot=/^(?:input|select|textarea|button|object)$/i,Rt=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Pe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),pe.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,i=pe.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Ot.test(e.nodeName)||Rt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),fe.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),fe.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),fe.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=pe(this),o=e.match(De)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Bt=e.location,Wt=pe.now(),It=/\?/,$t=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=pe.trim(t+"");return i&&!pe.trim(i.replace($t,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new e.DOMParser,n=r.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var zt=/#.*$/,Xt=/([?&])_=[^&]*/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Vt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Yt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Kt={},Qt={},Zt="*/".concat("*"),en=Bt.href,tn=Gt.exec(en.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:en,type:"GET",isLocal:Vt.test(tn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Kt),ajaxTransport:X(Qt),ajax:function(t,n){function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c=void 0,s=i||"",T.readyState=t>0?4:0,o=t>=200&&t<300||304===t,r&&(x=Y(d,T,r)),x=J(d,x,T,o),o?(d.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=T.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===d.type?C="nocontent":304===t?C="notmodified":(C=x.state,f=x.data,v=x.error,o=!v)):(v=C,!t&&C||(C="error",t<0&&(t=0))),T.status=t,T.statusText=(n||C)+"",o?g.resolveWith(p,[f,C,T]):g.rejectWith(p,[T,C,v]),T.statusCode(y),y=void 0,l&&h.trigger(o?"ajaxSuccess":"ajaxError",[T,d,o?f:v]),m.fireWith(p,[T,C]),l&&(h.trigger("ajaxComplete",[T,d]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,d=pe.ajaxSetup({},n),p=d.context||d,h=d.context&&(p.nodeType||p.jquery)?pe(p):pe.event,g=pe.Deferred(),m=pe.Callbacks("once memory"),y=d.statusCode||{},v={},x={},b=0,w="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!f)for(f={};t=Ut.exec(s);)f[t[1].toLowerCase()]=t[2];t=f[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=x[n]=x[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(b<2)for(t in e)y[t]=[y[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),r(0,t),this}};if(g.promise(T).complete=m.add,T.success=T.done,T.error=T.fail,d.url=((t||d.url||en)+"").replace(zt,"").replace(Jt,tn[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=pe.trim(d.dataType||"*").toLowerCase().match(De)||[""],null==d.crossDomain&&(i=Gt.exec(d.url.toLowerCase()),d.crossDomain=!(!i||i[1]===tn[1]&&i[2]===tn[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(tn[3]||("http:"===tn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=pe.param(d.data,d.traditional)),U(Kt,d,n,T),2===b)return T;l=pe.event&&d.global,l&&0===pe.active++&&pe.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Yt.test(d.type),a=d.url,d.hasContent||(d.data&&(a=d.url+=(It.test(a)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Xt.test(a)?a.replace(Xt,"$1_="+Wt++):a+(It.test(a)?"&":"?")+"_="+Wt++)),d.ifModified&&(pe.lastModified[a]&&T.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&T.setRequestHeader("If-None-Match",pe.etag[a])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",d.contentType),T.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Zt+"; q=0.01":""):d.accepts["*"]);for(o in d.headers)T.setRequestHeader(o,d.headers[o]);if(d.beforeSend&&(d.beforeSend.call(p,T,d)===!1||2===b))return T.abort();w="abort";for(o in{success:1,error:1,complete:1})T[o](d[o]);if(c=U(Qt,d,n,T)){if(T.readyState=1,l&&h.trigger("ajaxSend",[T,d]),2===b)return T;d.async&&d.timeout>0&&(u=e.setTimeout(function(){T.abort("timeout")},d.timeout));try{b=1,c.send(v,r)}catch(C){if(!(b<2))throw C;r(-1,C)}}else r(-1,"No Transport");return T},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,r,i){return pe.isFunction(n)&&(i=i||r,r=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:i,data:n,success:r},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return fe.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:K(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var nn=/%20/g,rn=/\[\]$/,on=/\r?\n/g,an=/^(?:submit|button|image|reset|file)$/i,sn=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,r=[],i=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){i(this.name,this.value)});else for(n in e)Q(n,e[n],t,i);return r.join("&").replace(nn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&sn.test(this.nodeName)&&!an.test(e)&&(this.checked||!Be.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():re.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var un=0,ln={},cn=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),fe.cors=!!cn&&"withCredentials"in cn,cn=fe.ajax=!!cn,cn&&pe.ajaxTransport(function(t){if(!t.crossDomain||fe.cors){var n;return{send:function(r,i){var o,a=t.xhr(),s=++un;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)void 0!==r[o]&&a.setRequestHeader(o,r[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,r){var o,u,l;if(n&&(r||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,r)4!==a.readyState&&a.abort();else{l={},o=a.status,"string"==typeof a.responseText&&(l.text=a.responseText);try{u=a.statusText}catch(c){u=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=l.text?200:404}l&&i(o,u,l,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=re.head||pe("head")[0]||re.documentElement;return{send:function(r,i){t=re.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var fn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=fn.pop()||pe.expando+"_"+Wt++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(It.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||pe.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?pe(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,fn.push(i)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||re;var r=Te.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=y([e],t,i),i&&i.length&&pe(i).remove(),pe.merge([],r.childNodes))};var pn=pe.fn.load;return pe.fn.load=function(e,t,n){if("string"!=typeof e&&pn)return pn.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&pe.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?pe("
    ").append(pe.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=pe.css(e,"position"),f=pe(e),d={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=pe.css(e,"top"),u=pe.css(e,"left"),l=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,u])>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+i),"using"in t?t.using.call(e,d):f.css(d)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,i)?("undefined"!=typeof i.getBoundingClientRect&&(r=i.getBoundingClientRect()),n=te(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===pe.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(r,"marginTop",!0),left:t.left-n.left-pe.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){ +for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){layui.$=pe,e("jquery",pe)}),pe}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/laydate.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/laydate.js new file mode 100644 index 0000000..6ddc3bf --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/laydate.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;!function(){"use strict";var e=window.layui&&layui.define,t={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,n=t.length-1,a=n;a>0;a--)if("interactive"===t[a].readyState){e=t[a].src;break}return e||t[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),getStyle:function(e,t){var n=e.currentStyle?e.currentStyle:window.getComputedStyle(e,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](t)},link:function(e,a,i){if(n.path){var r=document.getElementsByTagName("head")[0],o=document.createElement("link");"string"==typeof a&&(i=a);var s=(i||e).replace(/\.|\//g,""),l="layuicss-"+s,d=0;o.rel="stylesheet",o.href=n.path+e,o.id=l,document.getElementById(l)||r.appendChild(o),"function"==typeof a&&!function c(){return++d>80?window.console&&console.error("laydate.css: Invalid"):void(1989===parseInt(t.getStyle(document.getElementById(l),"width"))?a():setTimeout(c,100))}()}}},n={v:"5.0.9",config:{},index:window.laydate&&window.laydate.v?1e5:0,path:t.getPath,set:function(e){var t=this;return t.config=w.extend({},t.config,e),t},ready:function(a){var i="laydate",r="",o=(e?"modules/laydate/":"theme/")+"default/laydate.css?v="+n.v+r;return e?layui.addcss(o,a,i):t.link(o,a,i),this}},a=function(){var e=this;return{hint:function(t){e.hint.call(e,t)},config:e.config}},i="laydate",r=".layui-laydate",o="layui-this",s="laydate-disabled",l="开始日期超出了结束日期
    建议重新选择",d=[100,2e5],c="layui-laydate-static",m="layui-laydate-list",u="laydate-selected",h="layui-laydate-hint",y="laydate-day-prev",f="laydate-day-next",p="layui-laydate-footer",g=".laydate-btns-confirm",v="laydate-time-text",D=".laydate-btns-time",T=function(e){var t=this;t.index=++n.index,t.config=w.extend({},t.config,n.config,e),n.ready(function(){t.init()})},w=function(e){return new C(e)},C=function(e){for(var t=0,n="object"==typeof e?[e]:(this.selector=e,document.querySelectorAll(e||null));t0)return n[0].getAttribute(e)}():n.each(function(n,a){a.setAttribute(e,t)})},C.prototype.removeAttr=function(e){return this.each(function(t,n){n.removeAttribute(e)})},C.prototype.html=function(e){return this.each(function(t,n){n.innerHTML=e})},C.prototype.val=function(e){return this.each(function(t,n){n.value=e})},C.prototype.append=function(e){return this.each(function(t,n){"object"==typeof e?n.appendChild(e):n.innerHTML=n.innerHTML+e})},C.prototype.remove=function(e){return this.each(function(t,n){e?n.removeChild(e):n.parentNode.removeChild(n)})},C.prototype.on=function(e,t){return this.each(function(n,a){a.attachEvent?a.attachEvent("on"+e,function(e){e.target=e.srcElement,t.call(a,e)}):a.addEventListener(e,t,!1)})},C.prototype.off=function(e,t){return this.each(function(n,a){a.detachEvent?a.detachEvent("on"+e,t):a.removeEventListener(e,t,!1)})},T.isLeapYear=function(e){return e%4===0&&e%100!==0||e%400===0},T.prototype.config={type:"date",range:!1,format:"yyyy-MM-dd",value:null,isInitValue:!0,min:"1900-1-1",max:"2099-12-31",trigger:"focus",show:!1,showBottom:!0,btns:["clear","now","confirm"],lang:"cn",theme:"default",position:null,calendar:!1,mark:{},zIndex:null,done:null,change:null},T.prototype.lang=function(){var e=this,t=e.config,n={cn:{weeks:["日","一","二","三","四","五","六"],time:["时","分","秒"],timeTips:"选择时间",startTime:"开始时间",endTime:"结束时间",dateTips:"返回日期",month:["一","二","三","四","五","六","七","八","九","十","十一","十二"],tools:{confirm:"确定",clear:"清空",now:"现在"}},en:{weeks:["Su","Mo","Tu","We","Th","Fr","Sa"],time:["Hours","Minutes","Seconds"],timeTips:"Select Time",startTime:"Start Time",endTime:"End Time",dateTips:"Select Date",month:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],tools:{confirm:"Confirm",clear:"Clear",now:"Now"}}};return n[t.lang]||n.cn},T.prototype.init=function(){var e=this,t=e.config,n="yyyy|y|MM|M|dd|d|HH|H|mm|m|ss|s",a="static"===t.position,i={year:"yyyy",month:"yyyy-MM",date:"yyyy-MM-dd",time:"HH:mm:ss",datetime:"yyyy-MM-dd HH:mm:ss"};t.elem=w(t.elem),t.eventElem=w(t.eventElem),t.elem[0]&&(t.range===!0&&(t.range="-"),t.format===i.date&&(t.format=i[t.type]),e.format=t.format.match(new RegExp(n+"|.","g"))||[],e.EXP_IF="",e.EXP_SPLIT="",w.each(e.format,function(t,a){var i=new RegExp(n).test(a)?"\\d{"+function(){return new RegExp(n).test(e.format[0===t?t+1:t-1]||"")?/^yyyy|y$/.test(a)?4:a.length:/^yyyy$/.test(a)?"1,4":/^y$/.test(a)?"1,308":"1,2"}()+"}":"\\"+a;e.EXP_IF=e.EXP_IF+i,e.EXP_SPLIT=e.EXP_SPLIT+"("+i+")"}),e.EXP_IF=new RegExp("^"+(t.range?e.EXP_IF+"\\s\\"+t.range+"\\s"+e.EXP_IF:e.EXP_IF)+"$"),e.EXP_SPLIT=new RegExp("^"+e.EXP_SPLIT+"$",""),e.isInput(t.elem[0])||"focus"===t.trigger&&(t.trigger="click"),t.elem.attr("lay-key")||(t.elem.attr("lay-key",e.index),t.eventElem.attr("lay-key",e.index)),t.mark=w.extend({},t.calendar&&"cn"===t.lang?{"0-1-1":"元旦","0-2-14":"情人","0-3-8":"妇女","0-3-12":"植树","0-4-1":"愚人","0-5-1":"劳动","0-5-4":"青年","0-6-1":"儿童","0-9-10":"教师","0-9-18":"国耻","0-10-1":"国庆","0-12-25":"圣诞"}:{},t.mark),w.each(["min","max"],function(e,n){var a=[],i=[];if("number"==typeof t[n]){var r=t[n],o=(new Date).getTime(),s=864e5,l=new Date(r?r0)return!0;var a=w.elem("div",{"class":"layui-laydate-header"}),i=[function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-y"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-m"});return e.innerHTML="",e}(),function(){var e=w.elem("div",{"class":"laydate-set-ym"}),t=w.elem("span"),n=w.elem("span");return e.appendChild(t),e.appendChild(n),e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-m"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-y"});return e.innerHTML="",e}()],d=w.elem("div",{"class":"layui-laydate-content"}),c=w.elem("table"),m=w.elem("thead"),u=w.elem("tr");w.each(i,function(e,t){a.appendChild(t)}),m.appendChild(u),w.each(new Array(6),function(e){var t=c.insertRow(0);w.each(new Array(7),function(a){if(0===e){var i=w.elem("th");i.innerHTML=n.weeks[a],u.appendChild(i)}t.insertCell(a)})}),c.insertBefore(m,c.children[0]),d.appendChild(c),r[e]=w.elem("div",{"class":"layui-laydate-main laydate-main-list-"+e}),r[e].appendChild(a),r[e].appendChild(d),o.push(i),s.push(d),l.push(c)}),w(d).html(function(){var e=[],i=[];return"datetime"===t.type&&e.push(''+n.timeTips+""),w.each(t.btns,function(e,r){var o=n.tools[r]||"btn";t.range&&"now"===r||(a&&"clear"===r&&(o="cn"===t.lang?"重置":"Reset"),i.push(''+o+""))}),e.push('"),e.join("")}()),w.each(r,function(e,t){i.appendChild(t)}),t.showBottom&&i.appendChild(d),/^#/.test(t.theme)){var m=w.elem("style"),u=["#{{id}} .layui-laydate-header{background-color:{{theme}};}","#{{id}} .layui-this{background-color:{{theme}} !important;}"].join("").replace(/{{id}}/g,e.elemID).replace(/{{theme}}/g,t.theme);"styleSheet"in m?(m.setAttribute("type","text/css"),m.styleSheet.cssText=u):m.innerHTML=u,w(i).addClass("laydate-theme-molv"),i.appendChild(m)}e.remove(T.thisElemDate),a?t.elem.append(i):(document.body.appendChild(i),e.position()),e.checkDate().calendar(),e.changeEvent(),T.thisElemDate=e.elemID,"function"==typeof t.ready&&t.ready(w.extend({},t.dateTime,{month:t.dateTime.month+1}))},T.prototype.remove=function(e){var t=this,n=(t.config,w("#"+(e||t.elemID)));return n.hasClass(c)||t.checkDate(function(){n.remove()}),t},T.prototype.position=function(){var e=this,t=e.config,n=e.bindElem||t.elem[0],a=n.getBoundingClientRect(),i=e.elem.offsetWidth,r=e.elem.offsetHeight,o=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},s=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},l=5,d=a.left,c=a.bottom;d+i+l>s("width")&&(d=s("width")-i-l),c+r+l>s()&&(c=a.top>r?a.top-r:s()-r,c-=2*l),t.position&&(e.elem.style.position=t.position),e.elem.style.left=d+("fixed"===t.position?0:o(1))+"px",e.elem.style.top=c+("fixed"===t.position?0:o())+"px"},T.prototype.hint=function(e){var t=this,n=(t.config,w.elem("div",{"class":h}));n.innerHTML=e||"",w(t.elem).find("."+h).remove(),t.elem.appendChild(n),clearTimeout(t.hinTimer),t.hinTimer=setTimeout(function(){w(t.elem).find("."+h).remove()},3e3)},T.prototype.getAsYM=function(e,t,n){return n?t--:t++,t<0&&(t=11,e--),t>11&&(t=0,e++),[e,t]},T.prototype.systemDate=function(e){var t=e||new Date;return{year:t.getFullYear(),month:t.getMonth(),date:t.getDate(),hours:e?e.getHours():0,minutes:e?e.getMinutes():0,seconds:e?e.getSeconds():0}},T.prototype.checkDate=function(e){var t,a,i=this,r=(new Date,i.config),o=r.dateTime=r.dateTime||i.systemDate(),s=i.bindElem||r.elem[0],l=(i.isInput(s)?"val":"html",i.isInput(s)?s.value:"static"===r.position?"":s.innerHTML),c=function(e){e.year>d[1]&&(e.year=d[1],a=!0),e.month>11&&(e.month=11,a=!0),e.hours>23&&(e.hours=0,a=!0),e.minutes>59&&(e.minutes=0,e.hours++,a=!0),e.seconds>59&&(e.seconds=0,e.minutes++,a=!0),t=n.getEndDate(e.month+1,e.year),e.date>t&&(e.date=t,a=!0)},m=function(e,t,n){var o=["startTime","endTime"];t=(t.match(i.EXP_SPLIT)||[]).slice(1),n=n||0,r.range&&(i[o[n]]=i[o[n]]||{}),w.each(i.format,function(s,l){var c=parseFloat(t[s]);t[s].length必须遵循下述格式:
    "+(r.range?r.format+" "+r.range+" "+r.format:r.format)+"
    已为你重置"),a=!0):l&&l.constructor===Date?r.dateTime=i.systemDate(l):(r.dateTime=i.systemDate(),delete i.startState,delete i.endState,delete i.startDate,delete i.endDate,delete i.startTime,delete i.endTime),c(o),a&&l&&i.setValue(r.range?i.endDate?i.parse():"":i.parse()),e&&e(),i)},T.prototype.mark=function(e,t){var n,a=this,i=a.config;return w.each(i.mark,function(e,a){var i=e.split("-");i[0]!=t[0]&&0!=i[0]||i[1]!=t[1]&&0!=i[1]||i[2]!=t[2]||(n=a||t[2])}),n&&e.html(''+n+""),a},T.prototype.limit=function(e,t,n,a){var i,r=this,o=r.config,l={},d=o[n>41?"endDate":"dateTime"],c=w.extend({},d,t||{});return w.each({now:c,min:o.min,max:o.max},function(e,t){l[e]=r.newDate(w.extend({year:t.year,month:t.month,date:t.date},function(){var e={};return w.each(a,function(n,a){e[a]=t[a]}),e}())).getTime()}),i=l.nowl.max,e&&e[i?"addClass":"removeClass"](s),i},T.prototype.calendar=function(e){var t,a,i,r=this,s=r.config,l=e||s.dateTime,c=new Date,m=r.lang(),u="date"!==s.type&&"datetime"!==s.type,h=e?1:0,y=w(r.table[h]).find("td"),f=w(r.elemHeader[h][2]).find("span");if(l.yeard[1]&&(l.year=d[1],r.hint("最高只能支持到公元"+d[1]+"年")),r.firstDate||(r.firstDate=w.extend({},l)),c.setFullYear(l.year,l.month,1),t=c.getDay(),a=n.getEndDate(l.month||12,l.year),i=n.getEndDate(l.month+1,l.year),w.each(y,function(e,n){var d=[l.year,l.month],c=0;n=w(n),n.removeAttr("class"),e=t&&e=n.firstDate.year&&(r.month=a.max.month,r.date=a.max.date),n.limit(w(i),r,t),M++}),w(u[f?0:1]).attr("lay-ym",M-8+"-"+T[1]).html(b+p+" - "+(M-1+p))}else if("month"===e)w.each(new Array(12),function(e){var i=w.elem("li",{"lay-ym":e}),s={year:T[0],month:e};e+1==T[1]&&w(i).addClass(o),i.innerHTML=r.month[e]+(f?"月":""),d.appendChild(i),T[0]=n.firstDate.year&&(s.date=a.max.date),n.limit(w(i),s,t)}),w(u[f?0:1]).attr("lay-ym",T[0]+"-"+T[1]).html(T[0]+p);else if("time"===e){var E=function(){w(d).find("ol").each(function(e,a){w(a).find("li").each(function(a,i){n.limit(w(i),[{hours:a},{hours:n[x].hours,minutes:a},{hours:n[x].hours,minutes:n[x].minutes,seconds:a}][e],t,[["hours"],["hours","minutes"],["hours","minutes","seconds"]][e])})}),a.range||n.limit(w(n.footer).find(g),n[x],0,["hours","minutes","seconds"])};a.range?n[x]||(n[x]={hours:0,minutes:0,seconds:0}):n[x]=i,w.each([24,60,60],function(e,t){var a=w.elem("li"),i=["

    "+r.time[e]+"

      "];w.each(new Array(t),function(t){i.push(""+w.digit(t,2)+"")}),a.innerHTML=i.join("")+"
    ",d.appendChild(a)}),E()}if(y&&h.removeChild(y),h.appendChild(d),"year"===e||"month"===e)w(n.elemMain[t]).addClass("laydate-ym-show"),w(d).find("li").on("click",function(){var r=0|w(this).attr("lay-ym");if(!w(this).hasClass(s)){if(0===t)i[e]=r,l&&(n.startDate[e]=r),n.limit(w(n.footer).find(g),null,0);else if(l)n.endDate[e]=r;else{var c="year"===e?n.getAsYM(r,T[1]-1,"sub"):n.getAsYM(T[0],r,"sub");w.extend(i,{year:c[0],month:c[1]})}"year"===a.type||"month"===a.type?(w(d).find("."+o).removeClass(o),w(this).addClass(o),"month"===a.type&&"year"===e&&(n.listYM[t][0]=r,l&&(n[["startDate","endDate"][t]].year=r),n.list("month",t))):(n.checkDate("limit").calendar(),n.closeList()),n.setBtnStatus(),a.range||n.done(null,"change"),w(n.footer).find(D).removeClass(s)}});else{var S=w.elem("span",{"class":v}),k=function(){w(d).find("ol").each(function(e){var t=this,a=w(t).find("li");t.scrollTop=30*(n[x][C[e]]-2),t.scrollTop<=0&&a.each(function(e,n){if(!w(this).hasClass(s))return t.scrollTop=30*(e-2),!0})})},H=w(c[2]).find("."+v);k(),S.innerHTML=a.range?[r.startTime,r.endTime][t]:r.timeTips,w(n.elemMain[t]).addClass("laydate-time-show"),H[0]&&H.remove(),c[2].appendChild(S),w(d).find("ol").each(function(e){var t=this;w(t).find("li").on("click",function(){var r=0|this.innerHTML;w(this).hasClass(s)||(a.range?n[x][C[e]]=r:i[C[e]]=r,w(t).find("."+o).removeClass(o),w(this).addClass(o),E(),k(),(n.endDate||"time"===a.type)&&n.done(null,"change"),n.setBtnStatus())})})}return n},T.prototype.listYM=[],T.prototype.closeList=function(){var e=this;e.config;w.each(e.elemCont,function(t,n){w(this).find("."+m).remove(),w(e.elemMain[t]).removeClass("laydate-ym-show laydate-time-show")}),w(e.elem).find("."+v).remove()},T.prototype.setBtnStatus=function(e,t,n){var a,i=this,r=i.config,o=w(i.footer).find(g),d=r.range&&"date"!==r.type&&"time"!==r.type;d&&(t=t||i.startDate,n=n||i.endDate,a=i.newDate(t).getTime()>i.newDate(n).getTime(),i.limit(null,t)||i.limit(null,n)?o.addClass(s):o[a?"addClass":"removeClass"](s),e&&a&&i.hint("string"==typeof e?l.replace(/日期/g,e):l))},T.prototype.parse=function(e,t){var n=this,a=n.config,i=t||(e?w.extend({},n.endDate,n.endTime):a.range?w.extend({},n.startDate,n.startTime):a.dateTime),r=n.format.concat();return w.each(r,function(e,t){/yyyy|y/.test(t)?r[e]=w.digit(i.year,t.length):/MM|M/.test(t)?r[e]=w.digit(i.month+1,t.length):/dd|d/.test(t)?r[e]=w.digit(i.date,t.length):/HH|H/.test(t)?r[e]=w.digit(i.hours,t.length):/mm|m/.test(t)?r[e]=w.digit(i.minutes,t.length):/ss|s/.test(t)&&(r[e]=w.digit(i.seconds,t.length))}),a.range&&!e?r.join("")+" "+a.range+" "+n.parse(1):r.join("")},T.prototype.newDate=function(e){return e=e||{},new Date(e.year||1,e.month||0,e.date||1,e.hours||0,e.minutes||0,e.seconds||0)},T.prototype.setValue=function(e){var t=this,n=t.config,a=t.bindElem||n.elem[0],i=t.isInput(a)?"val":"html";return"static"===n.position||w(a)[i](e||""),this},T.prototype.stampRange=function(){var e,t,n=this,a=n.config,i=w(n.elem).find("td");if(a.range&&!n.endDate&&w(n.footer).find(g).addClass(s),n.endDate)return e=n.newDate({year:n.startDate.year,month:n.startDate.month,date:n.startDate.date}).getTime(),t=n.newDate({year:n.endDate.year,month:n.endDate.month,date:n.endDate.date}).getTime(),e>t?n.hint(l):void w.each(i,function(a,i){var r=w(i).attr("lay-ymd").split("-"),s=n.newDate({year:r[0],month:r[1]-1,date:r[2]}).getTime();w(i).removeClass(u+" "+o),s!==e&&s!==t||w(i).addClass(w(i).hasClass(y)||w(i).hasClass(f)?u:o),s>e&&s','
    '+f+"
    ",'
    ','',"
    ","
    "].join(""));return l.ie&&l.ie<8?c.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),s.call(a,m,c[0],y),c.addClass("layui-hide").after(m),a.index)},c.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},c.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},c.prototype.setContent=function(t,i,a){var l=u(t);l[0]&&(a?e(l[0].document.body).append(i):e(l[0].document.body).html(i),layedit.sync(t))},c.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},c.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var s=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),c=o.find("head"),s=e([""].join("")),u=o.find("body");c.append(s),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,c=e(r.body);c.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

    ")}}),e(n).parents("form").on("submit",function(){var t=c.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),c.on("paste",function(e){r.execCommand("formatBlock",!1,"

    "),setTimeout(function(){f.call(t,c),n.value=c.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),c={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o.render({url:r.url,method:r.type,elem:e(n).find("input")[0],done:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},s=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

    "),setTimeout(function(){o.focus()},10)):c[a]&&c[a].call(this,u),h.call(t,s,i)}},d=/image/;s.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,s),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

      ','
    • ','','
      ','',"
      ","
    • ",'
    • ','','
      ','",'","
      ","
    • ",'
    • ','','',"
    • ","
    "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
  • '+e+'
  • ')}),'
      '+t.join("")+"
    "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
      ','
    • ','','
      ','","
      ","
    • ",'
    • ','','
      ','',"
      ","
    • ",'
    • ','','',"
    • ","
    "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new c;t(n,w)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/layer.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/layer.js new file mode 100644 index 0000000..06d05fe --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/layer.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
    '+(f?r.title[0]:r.title)+"
    ":"";return r.zIndex=s,t([r.shade?'
    ':"",'
    '+(e&&2!=r.type?"":u)+'
    '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
    '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
    '+e+"
    "}():"")+(r.resize?'':"")+"
    "],u,i('
    ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(t){s=t.find(".layui-layer-input"),s.val(e.value||"").focus(),"function"==typeof f&&f(t)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"";return a}(),content:'
      '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
    • '+(t[0].content||"no content")+"
    • ";i'+(t[i].content||"no content")+"";return a}()+"
    ",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
    '+(u.length>1?'':"")+'
    '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
    ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
    是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/laypage.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/laypage.js new file mode 100644 index 0000000..0b55c45 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/laypage.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define(function(e){"use strict";var a=document,t="getElementById",n="getElementsByTagName",i="laypage",r="layui-disabled",u=function(e){var a=this;a.config=e||{},a.config.index=++s.index,a.render(!0)};u.prototype.type=function(){var e=this.config;if("object"==typeof e.elem)return void 0===e.elem.length?2:3},u.prototype.view=function(){var e=this,a=e.config,t=a.groups="groups"in a?0|a.groups:5;a.layout="object"==typeof a.layout?a.layout:["prev","page","next"],a.count=0|a.count,a.curr=0|a.curr||1,a.limits="object"==typeof a.limits?a.limits:[10,20,30,40,50],a.limit=0|a.limit||10,a.pages=Math.ceil(a.count/a.limit)||1,a.curr>a.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+"":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+"");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('');r<=u;r++)r===a.curr?e.push('"+r+""):e.push(''+r+"");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+"")),e.join("")}(),next:function(){return a.next?''+a.next+"":""}(),count:'共 '+a.count+" 条",limit:function(){var e=['"}(),refresh:['','',""].join(""),skip:function(){return['到第','','页',""].join("")}()};return['
    ',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
    "].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/laytpl.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/laytpl.js new file mode 100644 index 0000000..1acda2a --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/laytpl.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/mobile.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/mobile.js new file mode 100644 index 0000000..07cbd66 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/mobile.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define(function(i){i("layui.mobile",layui.v)});layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});layui.define(function(e){"use strict";var t=(window,document),i="querySelectorAll",n="getElementsByClassName",a=function(e){return t[i](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var i in e)t[i]=e[i];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var o=0,r=["layui-m-layer"],d=function(e){var t=this;t.config=l.extend(e),t.view()};d.prototype.view=function(){var e=this,i=e.config,s=t.createElement("div");e.id=s.id=r[0]+o,s.setAttribute("class",r[0]+" "+r[0]+(i.type||0)),s.setAttribute("index",o);var l=function(){var e="object"==typeof i.title;return i.title?'

    '+(e?i.title[0]:i.title)+"

    ":""}(),d=function(){"string"==typeof i.btn&&(i.btn=[i.btn]);var e,t=(i.btn||[]).length;return 0!==t&&i.btn?(e=''+i.btn[0]+"",2===t&&(e=''+i.btn[1]+""+e),'
    '+e+"
    "):""}();if(i.fixed||(i.top=i.hasOwnProperty("top")?i.top:100,i.style=i.style||"",i.style+=" top:"+(t.body.scrollTop+i.top)+"px"),2===i.type&&(i.content='

    '+(i.content||"")+"

    "),i.skin&&(i.anim="up"),"msg"===i.skin&&(i.shade=!1),s.innerHTML=(i.shade?"
    ':"")+'
    "+l+'
    '+i.content+"
    "+d+"
    ",!i.type||2===i.type){var y=t[n](r[0]+i.type),u=y.length;u>=1&&c.close(y[0].getAttribute("index"))}document.body.appendChild(s);var m=e.elem=a("#"+e.id)[0];i.success&&i.success(m),e.index=o++,e.action(i,m)},d.prototype.action=function(e,t){var i=this;e.time&&(l.timer[i.index]=setTimeout(function(){c.close(i.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),c.close(i.index)):e.yes?e.yes(i.index):c.close(i.index)};if(e.btn)for(var s=t[n]("layui-m-layerbtn")[0].children,o=s.length,r=0;r0&&e-1 in t)}function s(t){return A.call(t,function(t){return null!=t})}function u(t){return t.length>0?T.fn.concat.apply([],t):t}function c(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function l(t){return t in F?F[t]:F[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function f(t,e){return"number"!=typeof e||k[c(t)]?e:e+"px"}function h(t){var e,n;return $[t]||(e=L.createElement(t),L.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),$[t]=n),$[t]}function p(t){return"children"in t?D.call(t.children):T.map(t.childNodes,function(t){if(1==t.nodeType)return t})}function d(t,e){var n,r=t?t.length:0;for(n=0;n]*>/,R=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,Z=/^(?:body|html)$/i,q=/([A-Z])/g,H=["val","css","html","text","data","width","height","offset"],I=["after","prepend","before","append"],V=L.createElement("table"),_=L.createElement("tr"),B={tr:L.createElement("tbody"),tbody:V,thead:V,tfoot:V,td:_,th:_,"*":L.createElement("div")},U=/complete|loaded|interactive/,X=/^[\w-]*$/,J={},W=J.toString,Y={},G=L.createElement("div"),K={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},Q=Array.isArray||function(t){return t instanceof Array};return Y.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var r,i=t.parentNode,o=!i;return o&&(i=G).appendChild(t),r=~Y.qsa(i,e).indexOf(t),o&&G.removeChild(t),r},C=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},N=function(t){return A.call(t,function(e,n){return t.indexOf(e)==n})},Y.fragment=function(t,e,n){var r,i,a;return R.test(t)&&(r=T(L.createElement(RegExp.$1))),r||(t.replace&&(t=t.replace(z,"<$1>")),e===E&&(e=M.test(t)&&RegExp.$1),e in B||(e="*"),a=B[e],a.innerHTML=""+t,r=T.each(D.call(a.childNodes),function(){a.removeChild(this)})),o(n)&&(i=T(r),T.each(n,function(t,e){H.indexOf(t)>-1?i[t](e):i.attr(t,e)})),r},Y.Z=function(t,e){return new d(t,e)},Y.isZ=function(t){return t instanceof Y.Z},Y.init=function(t,n){var r;if(!t)return Y.Z();if("string"==typeof t)if(t=t.trim(),"<"==t[0]&&M.test(t))r=Y.fragment(t,RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}else{if(e(t))return T(L).ready(t);if(Y.isZ(t))return t;if(Q(t))r=s(t);else if(i(t))r=[t],t=null;else if(M.test(t))r=Y.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}}return Y.Z(r,t)},T=function(t,e){return Y.init(t,e)},T.extend=function(t){var e,n=D.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){m(t,n,e)}),t},Y.qsa=function(t,e){var n,r="#"==e[0],i=!r&&"."==e[0],o=r||i?e.slice(1):e,a=X.test(o);return t.getElementById&&a&&r?(n=t.getElementById(o))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:D.call(a&&!r&&t.getElementsByClassName?i?t.getElementsByClassName(o):t.getElementsByTagName(e):t.querySelectorAll(e))},T.contains=L.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},T.type=t,T.isFunction=e,T.isWindow=n,T.isArray=Q,T.isPlainObject=o,T.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},T.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},T.inArray=function(t,e,n){return O.indexOf.call(e,t,n)},T.camelCase=C,T.trim=function(t){return null==t?"":String.prototype.trim.call(t)},T.uuid=0,T.support={},T.expr={},T.noop=function(){},T.map=function(t,e){var n,r,i,o=[];if(a(t))for(r=0;r=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return O.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return e(t)?this.not(this.not(t)):T(A.call(this,function(e){return Y.matches(e,t)}))},add:function(t,e){return T(N(this.concat(T(t,e))))},is:function(t){return this.length>0&&Y.matches(this[0],t)},not:function(t){var n=[];if(e(t)&&t.call!==E)this.each(function(e){t.call(this,e)||n.push(this)});else{var r="string"==typeof t?this.filter(t):a(t)&&e(t.item)?D.call(t):T(t);this.forEach(function(t){r.indexOf(t)<0&&n.push(t)})}return T(n)},has:function(t){return this.filter(function(){return i(t)?T.contains(this,t):T(this).find(t).size()})},eq:function(t){return t===-1?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!i(t)?t:T(t)},last:function(){var t=this[this.length-1];return t&&!i(t)?t:T(t)},find:function(t){var e,n=this;return e=t?"object"==typeof t?T(t).filter(function(){var t=this;return O.some.call(n,function(e){return T.contains(e,t)})}):1==this.length?T(Y.qsa(this[0],t)):this.map(function(){return Y.qsa(this,t)}):T()},closest:function(t,e){var n=[],i="object"==typeof t&&T(t);return this.each(function(o,a){for(;a&&!(i?i.indexOf(a)>=0:Y.matches(a,t));)a=a!==e&&!r(a)&&a.parentNode;a&&n.indexOf(a)<0&&n.push(a)}),T(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=T.map(n,function(t){if((t=t.parentNode)&&!r(t)&&e.indexOf(t)<0)return e.push(t),t});return v(e,t)},parent:function(t){return v(N(this.pluck("parentNode")),t)},children:function(t){return v(this.map(function(){return p(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||D.call(this.childNodes)})},siblings:function(t){return v(this.map(function(t,e){return A.call(p(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return T.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=h(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var n=e(t);if(this[0]&&!n)var r=T(t).get(0),i=r.parentNode||this.length>1;return this.each(function(e){T(this).wrapAll(n?t.call(this,e):i?r.cloneNode(!0):r)})},wrapAll:function(t){if(this[0]){T(this[0]).before(t=T(t));for(var e;(e=t.children()).length;)t=e.first();T(t).append(this)}return this},wrapInner:function(t){var n=e(t);return this.each(function(e){var r=T(this),i=r.contents(),o=n?t.call(this,e):t;i.length?i.wrapAll(o):r.append(o)})},unwrap:function(){return this.parent().each(function(){T(this).replaceWith(T(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var e=T(this);(t===E?"none"==e.css("display"):t)?e.show():e.hide()})},prev:function(t){return T(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return T(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;T(this).empty().append(g(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=g(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(t,e){var n;return"string"!=typeof t||1 in arguments?this.each(function(n){if(1===this.nodeType)if(i(t))for(j in t)y(this,j,t[j]);else y(this,t,g(this,e,n,this.getAttribute(t)))}):0 in this&&1==this[0].nodeType&&null!=(n=this[0].getAttribute(t))?n:E},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){y(this,t)},this)})},prop:function(t,e){return t=K[t]||t,1 in arguments?this.each(function(n){this[t]=g(this,e,n,this[t])}):this[0]&&this[0][t]},removeProp:function(t){return t=K[t]||t,this.each(function(){delete this[t]})},data:function(t,e){var n="data-"+t.replace(q,"-$1").toLowerCase(),r=1 in arguments?this.attr(n,e):this.attr(n);return null!==r?b(r):E},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each(function(e){this.value=g(this,t,e,this.value)})):this[0]&&(this[0].multiple?T(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var n=T(this),r=g(this,t,e,n.offset()),i=n.offsetParent().offset(),o={top:r.top-i.top,left:r.left-i.left};"static"==n.css("position")&&(o.position="relative"),n.css(o)});if(!this.length)return null;if(L.documentElement!==this[0]&&!T.contains(L.documentElement,this[0]))return{top:0,left:0};var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(e,n){if(arguments.length<2){var r=this[0];if("string"==typeof e){if(!r)return;return r.style[C(e)]||getComputedStyle(r,"").getPropertyValue(e)}if(Q(e)){if(!r)return;var i={},o=getComputedStyle(r,"");return T.each(e,function(t,e){i[e]=r.style[C(e)]||o.getPropertyValue(e)}),i}}var a="";if("string"==t(e))n||0===n?a=c(e)+":"+f(e,n):this.each(function(){this.style.removeProperty(c(e))});else for(j in e)e[j]||0===e[j]?a+=c(j)+":"+f(j,e[j])+";":this.each(function(){this.style.removeProperty(c(j))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(T(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return!!t&&O.some.call(this,function(t){return this.test(x(t))},l(t))},addClass:function(t){return t?this.each(function(e){if("className"in this){S=[];var n=x(this),r=g(this,t,e,n);r.split(/\s+/g).forEach(function(t){T(this).hasClass(t)||S.push(t)},this),S.length&&x(this,n+(n?" ":"")+S.join(" "))}}):this},removeClass:function(t){return this.each(function(e){if("className"in this){if(t===E)return x(this,"");S=x(this),g(this,t,e,S).split(/\s+/g).forEach(function(t){S=S.replace(l(t)," ")}),x(this,S.trim())}})},toggleClass:function(t,e){return t?this.each(function(n){var r=T(this),i=g(this,t,n,x(this));i.split(/\s+/g).forEach(function(t){(e===E?!r.hasClass(t):e)?r.addClass(t):r.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var e="scrollTop"in this[0];return t===E?e?this[0].scrollTop:this[0].pageYOffset:this.each(e?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var e="scrollLeft"in this[0];return t===E?e?this[0].scrollLeft:this[0].pageXOffset:this.each(e?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),r=Z.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(T(t).css("margin-top"))||0,n.left-=parseFloat(T(t).css("margin-left"))||0,r.top+=parseFloat(T(e[0]).css("border-top-width"))||0,r.left+=parseFloat(T(e[0]).css("border-left-width"))||0,{top:n.top-r.top,left:n.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||L.body;t&&!Z.test(t.nodeName)&&"static"==T(t).css("position");)t=t.offsetParent;return t})}},T.fn.detach=T.fn.remove,["width","height"].forEach(function(t){var e=t.replace(/./,function(t){return t[0].toUpperCase()});T.fn[t]=function(i){var o,a=this[0];return i===E?n(a)?a["inner"+e]:r(a)?a.documentElement["scroll"+e]:(o=this.offset())&&o[t]:this.each(function(e){a=T(this),a.css(t,g(this,i,e,a[t]()))})}}),I.forEach(function(e,n){var r=n%2;T.fn[e]=function(){var e,i,o=T.map(arguments,function(n){var r=[];return e=t(n),"array"==e?(n.forEach(function(t){return t.nodeType!==E?r.push(t):T.zepto.isZ(t)?r=r.concat(t.get()):void(r=r.concat(Y.fragment(t)))}),r):"object"==e||null==n?n:Y.fragment(n)}),a=this.length>1;return o.length<1?this:this.each(function(t,e){i=r?e:e.parentNode,e=0==n?e.nextSibling:1==n?e.firstChild:2==n?e:null;var s=T.contains(L.documentElement,i);o.forEach(function(t){if(a)t=t.cloneNode(!0);else if(!i)return T(t).remove();i.insertBefore(t,e),s&&w(t,function(t){if(!(null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src)){var e=t.ownerDocument?t.ownerDocument.defaultView:window;e.eval.call(e,t.innerHTML)}})})})},T.fn[r?e+"To":"insert"+(n?"Before":"After")]=function(t){return T(t)[e](this),this}}),Y.Z.prototype=d.prototype=T.fn,Y.uniq=N,Y.deserializeValue=b,T.zepto=Y,T}();!function(t){function e(t){return t._zid||(t._zid=h++)}function n(t,n,o,a){if(n=r(n),n.ns)var s=i(n.ns);return(v[e(t)]||[]).filter(function(t){return t&&(!n.e||t.e==n.e)&&(!n.ns||s.test(t.ns))&&(!o||e(t.fn)===e(o))&&(!a||t.sel==a)})}function r(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function i(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function o(t,e){return t.del&&!y&&t.e in x||!!e}function a(t){return b[t]||y&&x[t]||t}function s(n,i,s,u,l,h,p){var d=e(n),m=v[d]||(v[d]=[]);i.split(/\s/).forEach(function(e){if("ready"==e)return t(document).ready(s);var i=r(e);i.fn=s,i.sel=l,i.e in b&&(s=function(e){var n=e.relatedTarget;if(!n||n!==this&&!t.contains(this,n))return i.fn.apply(this,arguments)}),i.del=h;var d=h||s;i.proxy=function(t){if(t=c(t),!t.isImmediatePropagationStopped()){t.data=u;var e=d.apply(n,t._args==f?[t]:[t].concat(t._args));return e===!1&&(t.preventDefault(),t.stopPropagation()),e}},i.i=m.length,m.push(i),"addEventListener"in n&&n.addEventListener(a(i.e),i.proxy,o(i,p))})}function u(t,r,i,s,u){var c=e(t);(r||"").split(/\s/).forEach(function(e){n(t,e,i,s).forEach(function(e){delete v[c][e.i],"removeEventListener"in t&&t.removeEventListener(a(e.e),e.proxy,o(e,u))})})}function c(e,n){return!n&&e.isDefaultPrevented||(n||(n=e),t.each(T,function(t,r){var i=n[t];e[t]=function(){return this[r]=w,i&&i.apply(n,arguments)},e[r]=E}),e.timeStamp||(e.timeStamp=Date.now()),(n.defaultPrevented!==f?n.defaultPrevented:"returnValue"in n?n.returnValue===!1:n.getPreventDefault&&n.getPreventDefault())&&(e.isDefaultPrevented=w)),e}function l(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===f||(n[e]=t[e]);return c(n,t)}var f,h=1,p=Array.prototype.slice,d=t.isFunction,m=function(t){return"string"==typeof t},v={},g={},y="onfocusin"in window,x={focus:"focusin",blur:"focusout"},b={mouseenter:"mouseover",mouseleave:"mouseout"};g.click=g.mousedown=g.mouseup=g.mousemove="MouseEvents",t.event={add:s,remove:u},t.proxy=function(n,r){var i=2 in arguments&&p.call(arguments,2);if(d(n)){var o=function(){return n.apply(r,i?i.concat(p.call(arguments)):arguments)};return o._zid=e(n),o}if(m(r))return i?(i.unshift(n[r],n),t.proxy.apply(null,i)):t.proxy(n[r],n);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,r){return this.on(t,e,n,r,1)};var w=function(){return!0},E=function(){return!1},j=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,T={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,n,r,i,o){var a,c,h=this;return e&&!m(e)?(t.each(e,function(t,e){h.on(t,n,r,e,o)}),h):(m(n)||d(i)||i===!1||(i=r,r=n,n=f),i!==f&&r!==!1||(i=r,r=f),i===!1&&(i=E),h.each(function(f,h){o&&(a=function(t){return u(h,t.type,i),i.apply(this,arguments)}),n&&(c=function(e){var r,o=t(e.target).closest(n,h).get(0);if(o&&o!==h)return r=t.extend(l(e),{currentTarget:o,liveFired:h}),(a||i).apply(o,[r].concat(p.call(arguments,1)))}),s(h,e,i,r,n,c||a)}))},t.fn.off=function(e,n,r){var i=this;return e&&!m(e)?(t.each(e,function(t,e){i.off(t,n,e)}),i):(m(n)||d(r)||r===!1||(r=n,n=f),r===!1&&(r=E),i.each(function(){u(this,e,r,n)}))},t.fn.trigger=function(e,n){return e=m(e)||t.isPlainObject(e)?t.Event(e):c(e),e._args=n,this.each(function(){e.type in x&&"function"==typeof this[e.type]?this[e.type]():"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,r){var i,o;return this.each(function(a,s){i=l(m(e)?t.Event(e):e),i._args=r,i.target=s,t.each(n(s,e.type||e),function(t,e){if(o=e.proxy(i),i.isImmediatePropagationStopped())return!1})}),o},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return 0 in arguments?this.bind(e,t):this.trigger(e)}}),t.Event=function(t,e){m(t)||(e=t,t=e.type);var n=document.createEvent(g[t]||"Events"),r=!0;if(e)for(var i in e)"bubbles"==i?r=!!e[i]:n[i]=e[i];return n.initEvent(t,r,!0),c(n)}}(e),function(t){function e(e,n,r){var i=t.Event(n);return t(e).trigger(i,r),!i.isDefaultPrevented()}function n(t,n,r,i){if(t.global)return e(n||x,r,i)}function r(e){e.global&&0===t.active++&&n(e,null,"ajaxStart")}function i(e){e.global&&!--t.active&&n(e,null,"ajaxStop")}function o(t,e){var r=e.context;return e.beforeSend.call(r,t,e)!==!1&&n(e,r,"ajaxBeforeSend",[t,e])!==!1&&void n(e,r,"ajaxSend",[t,e])}function a(t,e,r,i){var o=r.context,a="success";r.success.call(o,t,a,e),i&&i.resolveWith(o,[t,a,e]),n(r,o,"ajaxSuccess",[e,r,t]),u(a,e,r)}function s(t,e,r,i,o){var a=i.context;i.error.call(a,r,e,t),o&&o.rejectWith(a,[r,e,t]),n(i,a,"ajaxError",[r,i,t||e]),u(e,r,i)}function u(t,e,r){var o=r.context;r.complete.call(o,e,t),n(r,o,"ajaxComplete",[e,r]),i(r)}function c(t,e,n){if(n.dataFilter==l)return t;var r=n.context;return n.dataFilter.call(r,t,e)}function l(){}function f(t){return t&&(t=t.split(";",2)[0]),t&&(t==T?"html":t==j?"json":w.test(t)?"script":E.test(t)&&"xml")||"text"}function h(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function p(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()&&"jsonp"!=e.dataType||(e.url=h(e.url,e.data),e.data=void 0)}function d(e,n,r,i){return t.isFunction(n)&&(i=r,r=n,n=void 0),t.isFunction(r)||(i=r,r=void 0),{url:e,data:n,success:r,dataType:i}}function m(e,n,r,i){var o,a=t.isArray(n),s=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),i&&(n=r?i:i+"["+(s||"object"==o||"array"==o?n:"")+"]"),!i&&a?e.add(u.name,u.value):"array"==o||!r&&"object"==o?m(e,u,r,n):e.add(n,u)})}var v,g,y=+new Date,x=window.document,b=/)<[^<]*)*<\/script>/gi,w=/^(?:text|application)\/javascript/i,E=/^(?:text|application)\/xml/i,j="application/json",T="text/html",S=/^\s*$/,C=x.createElement("a");C.href=window.location.href,t.active=0,t.ajaxJSONP=function(e,n){if(!("type"in e))return t.ajax(e);var r,i,u=e.jsonpCallback,c=(t.isFunction(u)?u():u)||"Zepto"+y++,l=x.createElement("script"),f=window[c],h=function(e){t(l).triggerHandler("error",e||"abort")},p={abort:h};return n&&n.promise(p),t(l).on("load error",function(o,u){clearTimeout(i),t(l).off().remove(),"error"!=o.type&&r?a(r[0],p,e,n):s(null,u||"error",p,e,n),window[c]=f,r&&t.isFunction(f)&&f(r[0]),f=r=void 0}),o(p,e)===!1?(h("abort"),p):(window[c]=function(){r=arguments},l.src=e.url.replace(/\?(.+)=\?/,"?$1="+c),x.head.appendChild(l),e.timeout>0&&(i=setTimeout(function(){h("timeout")},e.timeout)),p)},t.ajaxSettings={type:"GET",beforeSend:l,success:l,error:l,complete:l,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:j,xml:"application/xml, text/xml",html:T,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0,dataFilter:l},t.ajax=function(e){var n,i,u=t.extend({},e||{}),d=t.Deferred&&t.Deferred();for(v in t.ajaxSettings)void 0===u[v]&&(u[v]=t.ajaxSettings[v]);r(u),u.crossDomain||(n=x.createElement("a"),n.href=u.url,n.href=n.href,u.crossDomain=C.protocol+"//"+C.host!=n.protocol+"//"+n.host),u.url||(u.url=window.location.toString()),(i=u.url.indexOf("#"))>-1&&(u.url=u.url.slice(0,i)),p(u);var m=u.dataType,y=/\?.+=\?/.test(u.url);if(y&&(m="jsonp"),u.cache!==!1&&(e&&e.cache===!0||"script"!=m&&"jsonp"!=m)||(u.url=h(u.url,"_="+Date.now())),"jsonp"==m)return y||(u.url=h(u.url,u.jsonp?u.jsonp+"=?":u.jsonp===!1?"":"callback=?")),t.ajaxJSONP(u,d);var b,w=u.accepts[m],E={},j=function(t,e){E[t.toLowerCase()]=[t,e]},T=/^([\w-]+:)\/\//.test(u.url)?RegExp.$1:window.location.protocol,N=u.xhr(),O=N.setRequestHeader;if(d&&d.promise(N),u.crossDomain||j("X-Requested-With","XMLHttpRequest"),j("Accept",w||"*/*"),(w=u.mimeType||w)&&(w.indexOf(",")>-1&&(w=w.split(",",2)[0]),N.overrideMimeType&&N.overrideMimeType(w)),(u.contentType||u.contentType!==!1&&u.data&&"GET"!=u.type.toUpperCase())&&j("Content-Type",u.contentType||"application/x-www-form-urlencoded"),u.headers)for(g in u.headers)j(g,u.headers[g]);if(N.setRequestHeader=j,N.onreadystatechange=function(){if(4==N.readyState){N.onreadystatechange=l,clearTimeout(b);var e,n=!1;if(N.status>=200&&N.status<300||304==N.status||0==N.status&&"file:"==T){if(m=m||f(u.mimeType||N.getResponseHeader("content-type")),"arraybuffer"==N.responseType||"blob"==N.responseType)e=N.response;else{e=N.responseText;try{e=c(e,m,u),"script"==m?(0,eval)(e):"xml"==m?e=N.responseXML:"json"==m&&(e=S.test(e)?null:t.parseJSON(e))}catch(r){n=r}if(n)return s(n,"parsererror",N,u,d)}a(e,N,u,d)}else s(N.statusText||null,N.status?"error":"abort",N,u,d)}},o(N,u)===!1)return N.abort(),s(null,"abort",N,u,d),N;var P=!("async"in u)||u.async;if(N.open(u.type,u.url,P,u.username,u.password),u.xhrFields)for(g in u.xhrFields)N[g]=u.xhrFields[g];for(g in E)O.apply(N,E[g]);return u.timeout>0&&(b=setTimeout(function(){N.onreadystatechange=l,N.abort(),s(null,"timeout",N,u,d)},u.timeout)),N.send(u.data?u.data:null),N},t.get=function(){return t.ajax(d.apply(null,arguments))},t.post=function(){var e=d.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=d.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,r){if(!this.length)return this;var i,o=this,a=e.split(/\s/),s=d(e,n,r),u=s.success;return a.length>1&&(s.url=a[0],i=a[1]),s.success=function(e){o.html(i?t("
    ").html(e.replace(b,"")).find(i):e),u&&u.apply(o,arguments)},t.ajax(s),this};var N=encodeURIComponent;t.param=function(e,n){var r=[];return r.add=function(e,n){t.isFunction(n)&&(n=n()),null==n&&(n=""),this.push(N(e)+"="+N(n))},m(r,e,n),r.join("&").replace(/%20/g,"+")}}(e),function(t){t.fn.serializeArray=function(){var e,n,r=[],i=function(t){return t.forEach?t.forEach(i):void r.push({name:e,value:t})};return this[0]&&t.each(this[0].elements,function(r,o){n=o.type,e=o.name,e&&"fieldset"!=o.nodeName.toLowerCase()&&!o.disabled&&"submit"!=n&&"reset"!=n&&"button"!=n&&"file"!=n&&("radio"!=n&&"checkbox"!=n||o.checked)&&i(t(o).val())}),r},t.fn.serialize=function(){var t=[];return this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(0 in arguments)this.bind("submit",e);else if(this.length){var n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return this}}(e),function(){try{getComputedStyle(void 0)}catch(t){var e=getComputedStyle;window.getComputedStyle=function(t,n){try{return e(t,n)}catch(r){return null}}}}(),t("zepto",e)});layui.define(["layer-mobile","zepto"],function(e){"use strict";var t=layui.zepto,a=layui["layer-mobile"],i=(layui.device(),"layui-upload-enter"),n="layui-upload-iframe",r={icon:2,shift:6},o={file:"文件",video:"视频",audio:"音频"};a.msg=function(e){return a.open({content:e||"",skin:"msg",time:2})};var s=function(e){this.options=e};s.prototype.init=function(){var e=this,a=e.options,r=t("body"),s=t(a.elem||".layui-upload-file"),u=t('');return t("#"+n)[0]||r.append(u),s.each(function(r,s){s=t(s);var u='
    ',l=s.attr("lay-type")||a.type;a.unwrap||(u='
    '+u+''+(s.attr("lay-title")||a.title||"上传"+(o[l]||"图片"))+"
    "),u=t(u),a.unwrap||u.on("dragover",function(e){e.preventDefault(),t(this).addClass(i)}).on("dragleave",function(){t(this).removeClass(i)}).on("drop",function(){t(this).removeClass(i)}),s.parent("form").attr("target")===n&&(a.unwrap?s.unwrap():(s.parent().next().remove(),s.unwrap().unwrap())),s.wrap(u),s.off("change").on("change",function(){e.action(this,l)})})},s.prototype.action=function(e,i){var o=this,s=o.options,u=e.value,l=t(e),p=l.attr("lay-ext")||s.ext||"";if(u){switch(i){case"file":if(p&&!RegExp("\\w\\.("+p+")$","i").test(escape(u)))return a.msg("不支持该文件格式",r),e.value="";break;case"video":if(!RegExp("\\w\\.("+(p||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(u)))return a.msg("不支持该视频格式",r),e.value="";break;case"audio":if(!RegExp("\\w\\.("+(p||"mp3|wav|mid")+")$","i").test(escape(u)))return a.msg("不支持该音频格式",r),e.value="";break;default:if(!RegExp("\\w\\.("+(p||"jpg|png|gif|bmp|jpeg")+")$","i").test(escape(u)))return a.msg("不支持该图片格式",r),e.value=""}s.before&&s.before(e),l.parent().submit();var c=t("#"+n),f=setInterval(function(){var t;try{t=c.contents().find("body").text()}catch(i){a.msg("上传接口存在跨域",r),clearInterval(f)}if(t){clearInterval(f),c.contents().find("body").html("");try{t=JSON.parse(t)}catch(i){return t={},a.msg("请对上传接口返回JSON字符",r)}"function"==typeof s.success&&s.success(t,e)}},30);e.value=""}},e("upload-mobile",function(e){var t=new s(e=e||{});t.init()})});layui.define(function(i){i("layim-mobile",layui.v)});layui["layui.mobile"]||layui.config({base:layui.cache.dir+"lay/modules/mobile/"}).extend({"layer-mobile":"layer-mobile",zepto:"zepto","upload-mobile":"upload-mobile","layim-mobile":"layim-mobile"}),layui.define(["layer-mobile","zepto","layim-mobile"],function(l){l("mobile",{layer:layui["layer-mobile"],layim:layui["layim-mobile"]})}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/rate.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/rate.js new file mode 100644 index 0000000..f86a7a5 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/rate.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var a=layui.jquery,i={config:{},index:layui.rate?layui.rate.index+1e4:0,set:function(e){var i=this;return i.config=a.extend({},i.config,e),i},on:function(e,a){return layui.onevent.call(this,n,e,a)}},l=function(){var e=this,a=e.config;return{setvalue:function(a){e.setvalue.call(e,a)},config:a}},n="rate",t="layui-rate",o="layui-icon-rate",s="layui-icon-rate-solid",u="layui-icon-rate-half",r="layui-icon-rate-solid layui-icon-rate-half",c="layui-icon-rate-solid layui-icon-rate",f="layui-icon-rate layui-icon-rate-half",v=function(e){var l=this;l.index=++i.index,l.config=a.extend({},l.config,i.config,e),l.render()};v.prototype.config={length:5,text:!1,readonly:!1,half:!1,value:0,theme:""},v.prototype.render=function(){var e=this,i=e.config,l=i.theme?'style="color: '+i.theme+';"':"";i.elem=a(i.elem),parseInt(i.value)!==i.value&&(i.half||(i.value=Math.ceil(i.value)-i.value<.5?Math.ceil(i.value):Math.floor(i.value)));for(var n='
      ",u=1;u<=i.length;u++){var r='
    • ";i.half&&parseInt(i.value)!==i.value&&u==Math.ceil(i.value)?n=n+'
    • ":n+=r}n+="
    "+(i.text?''+i.value+"星":"")+"";var c=i.elem,f=c.next("."+t);f[0]&&f.remove(),e.elemTemp=a(n),i.span=e.elemTemp.next("span"),i.setText&&i.setText(i.value),c.html(e.elemTemp),c.addClass("layui-inline"),i.readonly||e.action()},v.prototype.setvalue=function(e){var a=this,i=a.config;i.value=e,a.render()},v.prototype.action=function(){var e=this,i=e.config,l=e.elemTemp,n=l.find("i").width();l.children("li").each(function(e){var t=e+1,v=a(this);v.on("click",function(e){if(i.value=t,i.half){var o=e.pageX-a(this).offset().left;o<=n/2&&(i.value=i.value-.5)}i.text&&l.next("span").text(i.value+"星"),i.choose&&i.choose(i.value),i.setText&&i.setText(i.value)}),v.on("mousemove",function(e){if(l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+t+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half){var c=e.pageX-a(this).offset().left;c<=n/2&&v.children("i").addClass(u).removeClass(s)}}),v.on("mouseleave",function(){l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+Math.floor(i.value)+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half&&parseInt(i.value)!==i.value&&l.children("li:eq("+Math.floor(i.value)+")").children("i").addClass(u).removeClass(c)})})},v.prototype.events=function(){var e=this;e.config},i.render=function(e){var a=new v(e);return l.call(a)},e(n,i)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/table.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/table.js new file mode 100644 index 0000000..f72b2dd --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/table.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define(["laytpl","laypage","layer","form"],function(e){"use strict";var t=layui.$,i=layui.laytpl,a=layui.laypage,l=layui.layer,n=layui.form,o=layui.hint(),r=layui.device(),d={config:{checkName:"LAY_CHECKED",indexName:"LAY_TABLE_INDEX"},cache:{},index:layui.table?layui.table.index+1e4:0,set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,s,e,t)}},c=function(){var e=this,t=e.config,i=t.id;return i&&(c.config[i]=t),{reload:function(t){e.reload.call(e,t)},config:t}},s="table",u=".layui-table",h="layui-hide",f="layui-none",y="layui-table-view",p=".layui-table-header",m=".layui-table-body",v=".layui-table-main",g=".layui-table-fixed",x=".layui-table-fixed-l",b=".layui-table-fixed-r",k=".layui-table-tool",C=".layui-table-page",w=".layui-table-sort",N="layui-table-edit",T="layui-table-hover",F=function(e){var t='{{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}}';return e=e||{},['',"","{{# layui.each(d.data.cols, function(i1, item1){ }}","","{{# layui.each(item1, function(i2, item2){ }}",'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}','{{# if(item2.fixed === "right"){ right = true; } }}',function(){return e.fixed&&"right"!==e.fixed?'{{# if(item2.fixed && item2.fixed !== "right"){ }}':"right"===e.fixed?'{{# if(item2.fixed === "right"){ }}':""}(),'",e.fixed?"{{# }; }}":"","{{# }); }}","","{{# }); }}","","
    ','
    1){ }}","group","{{# } else { }}","{{d.index}}-{{item2.field || i2}}",'{{# if(item2.type !== "normal"){ }}'," laytable-cell-{{ item2.type }}","{{# } }}","{{# } }}",'" {{#if(item2.align){}}align="{{item2.align}}"{{#}}}>','{{# if(item2.type === "checkbox"){ }}','',"{{# } else { }}",'{{item2.title||""}}',"{{# if(!(item2.colspan > 1) && item2.sort){ }}",'',"{{# } }}","{{# } }}","
    ","
    "].join("")},W=['',"","
    "].join(""),z=['
    ',"{{# if(d.data.toolbar){ }}",'
    ',"{{# } }}",'
    ',"{{# var left, right; }}",'
    ',F(),"
    ",'
    ',W,"
    ","{{# if(left){ }}",'
    ','
    ',F({fixed:!0}),"
    ",'
    ',W,"
    ","
    ","{{# }; }}","{{# if(right){ }}",'
    ','
    ',F({fixed:"right"}),'
    ',"
    ",'
    ',W,"
    ","
    ","{{# }; }}","
    ","{{# if(d.data.page){ }}",'
    ','
    ',"
    ","{{# } }}","","
    "].join(""),A=t(window),S=t(document),M=function(e){var i=this;i.index=++d.index,i.config=t.extend({},i.config,d.config,e),i.render()};M.prototype.config={limit:10,loading:!0,cellMinWidth:60,text:{none:"无数据"}},M.prototype.render=function(){var e=this,a=e.config;if(a.elem=t(a.elem),a.where=a.where||{},a.id=a.id||a.elem.attr("id"),a.request=t.extend({pageName:"page",limitName:"limit"},a.request),a.response=t.extend({statusName:"code",statusCode:0,msgName:"msg",dataName:"data",countName:"count"},a.response),"object"==typeof a.page&&(a.limit=a.page.limit||a.limit,a.limits=a.page.limits||a.limits,e.page=a.page.curr=a.page.curr||1,delete a.page.elem,delete a.page.jump),!a.elem[0])return e;e.setArea();var l=a.elem,n=l.next("."+y),o=e.elem=t(i(z).render({VIEW_CLASS:y,data:a,index:e.index}));if(a.index=e.index,n[0]&&n.remove(),l.after(o),e.layHeader=o.find(p),e.layMain=o.find(v),e.layBody=o.find(m),e.layFixed=o.find(g),e.layFixLeft=o.find(x),e.layFixRight=o.find(b),e.layTool=o.find(k),e.layPage=o.find(C),e.layTool.html(i(t(a.toolbar).html()||"").render(a)),a.height&&e.fullSize(),a.cols.length>1){var r=e.layFixed.find(p).find("th");r.height(e.layHeader.height()-1-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom")))}e.pullData(e.page),e.events()},M.prototype.initOpts=function(e){var t=this,i=(t.config,{checkbox:48,space:15,numbers:40});e.checkbox&&(e.type="checkbox"),e.space&&(e.type="space"),e.type||(e.type="normal"),"normal"!==e.type&&(e.unresize=!0,e.width=e.width||i[e.type])},M.prototype.setArea=function(){var e=this,t=e.config,i=0,a=0,l=0,n=0,o=t.width||function(){var e=function(i){var a,l;i=i||t.elem.parent(),a=i.width();try{l="none"===i.css("display")}catch(n){}return!i[0]||a&&!l?a:e(i.parent())};return e()}();e.eachCols(function(){i++}),o-=function(){return"line"===t.skin||"nob"===t.skin?2:i+1}(),layui.each(t.cols,function(t,i){layui.each(i,function(t,l){var r;return l?(e.initOpts(l),r=l.width||0,void(l.colspan>1||(/\d+%$/.test(r)?l.width=r=Math.floor(parseFloat(r)/100*o):r||(l.width=r=0,a++),n+=r))):void i.splice(t,1)})}),e.autoColNums=a,o>n&&a&&(l=(o-n)/a),layui.each(t.cols,function(e,i){layui.each(i,function(e,i){var a=i.minWidth||t.cellMinWidth;i.colspan>1||0===i.width&&(i.width=Math.floor(l>=a?l:a))})}),t.height&&/^full-\d+$/.test(t.height)&&(e.fullHeightGap=t.height.split("-")[1],t.height=A.height()-e.fullHeightGap)},M.prototype.reload=function(e){var i=this;i.config.data&&i.config.data.constructor===Array&&delete i.config.data,i.config=t.extend({},i.config,e),i.render()},M.prototype.page=1,M.prototype.pullData=function(e,i){var a=this,n=a.config,o=n.request,r=n.response,d=function(){"object"==typeof n.initSort&&a.sort(n.initSort.field,n.initSort.type)};if(a.startTime=(new Date).getTime(),n.url){var c={};c[o.pageName]=e,c[o.limitName]=n.limit;var s=t.extend(c,n.where);n.contentType&&0==n.contentType.indexOf("application/json")&&(s=JSON.stringify(s)),t.ajax({type:n.method||"get",url:n.url,contentType:n.contentType,data:s,dataType:"json",headers:n.headers||{},success:function(t){t[r.statusName]!=r.statusCode?(a.renderForm(),a.layMain.html('
    '+(t[r.msgName]||"返回的数据状态异常")+"
    ")):(a.renderData(t,e,t[r.countName]),d(),n.time=(new Date).getTime()-a.startTime+" ms"),i&&l.close(i),"function"==typeof n.done&&n.done(t,e,t[r.countName])},error:function(e,t){a.layMain.html('
    数据接口请求异常
    '),a.renderForm(),i&&l.close(i)}})}else if(n.data&&n.data.constructor===Array){var u={},h=e*n.limit-n.limit;u[r.dataName]=n.data.concat().splice(h,n.limit),u[r.countName]=n.data.length,a.renderData(u,e,n.data.length),d(),"function"==typeof n.done&&n.done(u,e,u[r.countName])}},M.prototype.eachCols=function(e){var i=t.extend(!0,[],this.config.cols),a=[],l=0;layui.each(i,function(e,t){layui.each(t,function(t,n){if(n.colspan>1){var o=0;l++,n.CHILD_COLS=[],layui.each(i[e+1],function(e,t){t.PARENT_COL||o==n.colspan||(t.PARENT_COL=l,n.CHILD_COLS.push(t),o+=t.colspan>1?t.colspan:1)})}n.PARENT_COL||a.push(n)})});var n=function(t){layui.each(t||a,function(t,i){return i.CHILD_COLS?n(i.CHILD_COLS):void e(t,i)})};n()},M.prototype.renderData=function(e,n,o,r){var c=this,s=c.config,u=e[s.response.dataName]||[],y=[],p=[],m=[],v=function(){return!r&&c.sortKey?c.sort(c.sortKey.field,c.sortKey.sort,!0):(layui.each(u,function(e,a){var l=[],o=[],u=[],h=e+s.limit*(n-1)+1;0!==a.length&&(r||(a[d.config.indexName]=e),c.eachCols(function(e,n){var r=n.field||e,f=a[r];c.getColElem(c.layHeader,r);if(void 0!==f&&null!==f||(f=""),!(n.colspan>1)){var y=['",'
    '+function(){var e=t.extend(!0,{LAY_INDEX:h},a);return"checkbox"===n.type?'":"numbers"===n.type?h:n.toolbar?i(t(n.toolbar).html()||"").render(e):n.templet?function(){return"function"==typeof n.templet?n.templet(e):i(t(n.templet).html()||String(f)).render(e)}():f}(),"
    "].join("");l.push(y),n.fixed&&"right"!==n.fixed&&o.push(y),"right"===n.fixed&&u.push(y)}}),y.push(''+l.join("")+""),p.push(''+o.join("")+""),m.push(''+u.join("")+""))}),c.layBody.scrollTop(0),c.layMain.find("."+f).remove(),c.layMain.find("tbody").html(y.join("")),c.layFixLeft.find("tbody").html(p.join("")),c.layFixRight.find("tbody").html(m.join("")),c.renderForm(),c.syncCheckAll(),c.haveInit?c.scrollPatch():setTimeout(function(){c.scrollPatch()},50),c.haveInit=!0,void l.close(c.tipsIndex))};return c.key=s.id||s.index,d.cache[c.key]=u,c.layPage[0===u.length&&1==n?"addClass":"removeClass"](h),r?v():0===u.length?(c.renderForm(),c.layFixed.remove(),c.layMain.find("tbody").html(""),c.layMain.find("."+f).remove(),c.layMain.append('
    '+s.text.none+"
    ")):(v(),void(s.page&&(s.page=t.extend({elem:"layui-table-page"+s.index,count:o,limit:s.limit,limits:s.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(c.page=e.curr,s.limit=e.limit,c.pullData(e.curr,c.loading()))}},s.page),s.page.count=o,a.render(s.page))))},M.prototype.getColElem=function(e,t){var i=this,a=i.config;return e.eq(0).find(".laytable-cell-"+(a.index+"-"+t)+":eq(0)")},M.prototype.renderForm=function(e){n.render(e,"LAY-table-"+this.index)},M.prototype.sort=function(e,i,a,l){var n,r,c=this,u={},h=c.config,f=h.elem.attr("lay-filter"),y=d.cache[c.key];"string"==typeof e&&c.layHeader.find("th").each(function(i,a){var l=t(this),o=l.data("field");if(o===e)return e=l,n=o,!1});try{var n=n||e.data("field");if(c.sortKey&&!a&&n===c.sortKey.field&&i===c.sortKey.sort)return;var p=c.layHeader.find("th .laytable-cell-"+h.index+"-"+n).find(w);c.layHeader.find("th").find(w).removeAttr("lay-sort"),p.attr("lay-sort",i||null),c.layFixed.find("th")}catch(m){return o.error("Table modules: Did not match to field")}c.sortKey={field:n,sort:i},"asc"===i?r=layui.sort(y,n):"desc"===i?r=layui.sort(y,n,!0):(r=layui.sort(y,d.config.indexName),delete c.sortKey),u[h.response.dataName]=r,c.renderData(u,c.page,c.count,!0),l&&layui.event.call(e,s,"sort("+f+")",{field:n,type:i})},M.prototype.loading=function(){var e=this,t=e.config;if(t.loading&&t.url)return l.msg("数据请求中",{icon:16,offset:[e.elem.offset().top+e.elem.height()/2-35-A.scrollTop()+"px",e.elem.offset().left+e.elem.width()/2-90-A.scrollLeft()+"px"],time:-1,anim:-1,fixed:!1})},M.prototype.setCheckData=function(e,t){var i=this,a=i.config,l=d.cache[i.key];l[e]&&l[e].constructor!==Array&&(l[e][a.checkName]=t)},M.prototype.syncCheckAll=function(){var e=this,t=e.config,i=e.layHeader.find('input[name="layTableCheckbox"]'),a=function(i){return e.eachCols(function(e,a){"checkbox"===a.type&&(a[t.checkName]=i)}),i};i[0]&&(d.checkStatus(e.key).isAll?(i[0].checked||(i.prop("checked",!0),e.renderForm("checkbox")),a(!0)):(i[0].checked&&(i.prop("checked",!1),e.renderForm("checkbox")),a(!1)))},M.prototype.getCssRule=function(e,t){var i=this,a=i.elem.find("style")[0],l=a.sheet||a.styleSheet||{},n=l.cssRules||l.rules;layui.each(n,function(a,l){if(l.selectorText===".laytable-cell-"+i.index+"-"+e)return t(l),!0})},M.prototype.fullSize=function(){var e,t=this,i=t.config,a=i.height;t.fullHeightGap&&(a=A.height()-t.fullHeightGap,a<135&&(a=135),t.elem.css("height",a)),e=parseFloat(a)-parseFloat(t.layHeader.height())-1,i.toolbar&&(e-=t.layTool.outerHeight()),i.page&&(e=e-t.layPage.outerHeight()-1),t.layMain.css("height",e)},M.prototype.getScrollWidth=function(e){var t=0;return e?t=e.offsetWidth-e.clientWidth:(e=document.createElement("div"),e.style.width="100px",e.style.height="100px",e.style.overflowY="scroll",document.body.appendChild(e),t=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),t},M.prototype.scrollPatch=function(){var e=this,i=e.layMain.children("table"),a=e.layMain.width()-e.layMain.prop("clientWidth"),l=e.layMain.height()-e.layMain.prop("clientHeight"),n=e.getScrollWidth(e.layMain[0]),o=i.outerWidth()-e.layMain.width();if(e.autoColNums&&o<5&&!e.scrollPatchWStatus){var r=e.layHeader.eq(0).find("thead th:last-child"),d=r.data("field");e.getCssRule(d,function(t){var i=t.style.width||r.outerWidth();t.style.width=parseFloat(i)-n-o+"px",e.layMain.height()-e.layMain.prop("clientHeight")>0&&(t.style.width=parseFloat(t.style.width)-1+"px"),e.scrollPatchWStatus=!0})}if(a&&l){if(!e.elem.find(".layui-table-patch")[0]){var c=t('
    ');c.find("div").css({width:a}),e.layHeader.eq(0).find("thead tr").append(c)}}else e.layHeader.eq(0).find(".layui-table-patch").remove();var s=e.layMain.height(),u=s-l;e.layFixed.find(m).css("height",i.height()>u?u:"auto"),e.layFixRight[o>0?"removeClass":"addClass"](h),e.layFixRight.css("right",a-1)},M.prototype.events=function(){var e,a=this,n=a.config,o=t("body"),c={},u=a.layHeader.find("th"),h=".layui-table-cell",f=n.elem.attr("lay-filter");u.on("mousemove",function(e){var i=t(this),a=i.offset().left,l=e.clientX-a;i.attr("colspan")>1||i.data("unresize")||c.resizeStart||(c.allowResize=i.width()-l<=10,o.css("cursor",c.allowResize?"col-resize":""))}).on("mouseleave",function(){t(this);c.resizeStart||o.css("cursor","")}).on("mousedown",function(e){var i=t(this);if(c.allowResize){var l=i.data("field");e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],a.getCssRule(l,function(e){var t=e.style.width||i.outerWidth();c.rule=e,c.ruleWidth=parseFloat(t),c.minWidth=i.data("minwidth")||n.cellMinWidth})}}),S.on("mousemove",function(t){if(c.resizeStart){if(t.preventDefault(),c.rule){var i=c.ruleWidth+t.clientX-c.offset[0];i');d[0].value=e.data("content")||o.text(),e.find("."+N)[0]||e.append(d),d.focus()}else o.find(".layui-form-switch,.layui-form-checkbox")[0]||Math.round(o.prop("scrollWidth"))>Math.round(o.outerWidth())&&(a.tipsIndex=l.tips(['
    ',o.html(),"
    ",''].join(""),o[0],{tips:[3,""],time:-1,anim:-1,maxWidth:r.ios||r.android?300:600,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){l.close(t)})}}))}),a.layBody.on("click","*[lay-event]",function(){var e=t(this),l=e.parents("tr").eq(0).data("index"),n=a.layBody.find('tr[data-index="'+l+'"]'),o="layui-table-click",r=d.cache[a.key][l];layui.event.call(this,s,"tool("+f+")",{data:d.clearCacheKey(r),event:e.attr("lay-event"),tr:n,del:function(){d.cache[a.key][l]=[],n.remove(),a.scrollPatch()},update:function(e){e=e||{},layui.each(e,function(e,l){if(e in r){var o,d=n.children('td[data-field="'+e+'"]');r[e]=l,a.eachCols(function(t,i){i.field==e&&i.templet&&(o=i.templet)}),d.children(h).html(o?i(t(o).html()||l).render(r):l),d.data("content",l)}})}}),n.addClass(o).siblings("tr").removeClass(o)}),a.layMain.on("scroll",function(){var e=t(this),i=e.scrollLeft(),n=e.scrollTop();a.layHeader.scrollLeft(i),a.layFixed.find(m).scrollTop(n),l.close(a.tipsIndex)}),A.on("resize",function(){a.fullSize(),a.scrollPatch()})},d.init=function(e,i){i=i||{};var a=this,l=t(e?'table[lay-filter="'+e+'"]':u+"[lay-data]"),n="Table element property lay-data configuration item has a syntax error: ";return l.each(function(){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){o.error(n+l)}var c=[],s=t.extend({elem:this,cols:[],data:[],skin:a.attr("lay-skin"),size:a.attr("lay-size"),even:"string"==typeof a.attr("lay-even")},d.config,i,l);e&&a.hide(),a.find("thead>tr").each(function(e){s.cols[e]=[],t(this).children().each(function(i){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){return o.error(n+l)}var d=t.extend({title:a.text(),colspan:a.attr("colspan")||0,rowspan:a.attr("rowspan")||0},l);d.colspan<2&&c.push(d),s.cols[e].push(d)})}),a.find("tbody>tr").each(function(e){var i=t(this),a={};i.children("td").each(function(e,i){var l=t(this),n=l.data("field");if(n)return a[n]=l.html()}),layui.each(c,function(e,t){var l=i.children("td").eq(e);a[t.field]=l.html()}),s.data[e]=a}),d.render(s)}),a},d.checkStatus=function(e){var t=0,i=0,a=[],l=d.cache[e]||[];return layui.each(l,function(e,l){return l.constructor===Array?void i++:void(l[d.config.checkName]&&(t++,a.push(d.clearCacheKey(l))))}),{data:a,isAll:!!l.length&&t===l.length-i}},c.config={},d.reload=function(e,i){var a=c.config[e];return i=i||{},a?(i.data&&i.data.constructor===Array&&delete a.data,d.render(t.extend(!0,{},a,i))):o.error("The ID option was not found in the table instance")},d.render=function(e){var t=new M(e);return c.call(t)},d.clearCacheKey=function(e){return e=t.extend({},e),delete e[d.config.checkName],delete e[d.config.indexName],e},d.init(),e(s,d)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/tree.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/tree.js new file mode 100644 index 0000000..8bfd6bd --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/tree.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var o=layui.$,a=layui.hint(),i="layui-tree-enter",r=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};r.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},r.prototype.tree=function(e,a){var i=this,r=i.options,n=a||r.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
      '),s=o(["
    • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return r.check?''+("checkbox"===r.check?t.checkbox[0]:"radio"===r.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
    • "].join(""));l&&(s.append(c),i.tree(c,n.children)),e.append(s),"function"==typeof r.click&&i.click(s,n),i.spread(s,n),r.drag&&i.drag(s,n)})},r.prototype.click=function(e,o){var a=this,i=a.options;e.children("a").on("click",function(e){layui.stope(e),i.click(o)})},r.prototype.spread=function(e,o){var a=this,i=(a.options,e.children(".layui-tree-spread")),r=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),r.removeClass("layui-show"),i.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),r.addClass("layui-show"),i.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};r[0]&&(i.on("click",l),n.on("dblclick",l))},r.prototype.on=function(e){var a=this,r=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),r.drag&&o(document).on("mousemove",function(e){var i=a.move;if(i.from){var r=(i.to,o('
      '));e.preventDefault(),o("."+t)[0]||o("body").append(r);var n=o("."+t)[0]?o("."+t):r;n.addClass("layui-show").html(i.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(i),e.to&&e.to.elem.children("a").removeClass(i),a.move={},o("."+t).remove())})},r.prototype.move={},r.prototype.drag=function(e,a){var r=this,t=(r.options,e.children("a")),n=function(){var t=o(this),n=r.move;n.from&&(n.to={item:a,elem:e},t.addClass(i))};t.on("mousedown",function(){var o=r.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=r.move;a.from&&(delete a.to,e.removeClass(i))})},e("tree",function(e){var i=new r(e=e||{}),t=o(e.elem);return t[0]?void i.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/upload.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/upload.js new file mode 100644 index 0000000..6577f3e --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/upload.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,n=layui.hint(),a=layui.device(),o={config:{},set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,r,e,i)}},l=function(){var e=this;return{upload:function(i){e.upload.call(e,i)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var t=this;t.config=i.extend({},t.config,o.config,e),t.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var t=this,e=t.config;e.elem=i(e.elem),e.bindAction=i(e.bindAction),t.file(),t.events()},p.prototype.file=function(){var e=this,t=e.config,n=e.elemFile=i(['"].join("")),o=t.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&t.elem.wrap('
      '),e.isFile()?(e.elemFile=t.elem,t.field=t.elem[0].name):t.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,t=e.config,n=i(''),a=i(['
      ',"
      "].join(""));i("#"+f)[0]||i("body").append(n),t.elem.next().hasClass(c)||(e.elemFile.wrap(a),t.elem.next("."+c).append(function(){var e=[];return layui.each(t.data,function(i,t){t="function"==typeof t?t():t,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return t.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var i=this;window.FileReader&&layui.each(i.chooseFiles,function(i,t){var n=new FileReader;n.readAsDataURL(t),n.onload=function(){e&&e(i,t,this.result)}})},p.prototype.upload=function(e,t){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var t=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&t+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:t,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,i){i="function"==typeof i?i():i,r.append(e,i)}),i.ajax({url:l.url,type:l.method,data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(i){t++,d(e,i),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=i("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var i,t=e.contents().find("body");try{i=t.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}i&&(clearInterval(p.timer),t.html(""),d(0,i))},30)},d=function(e,i){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof i)try{i=JSON.parse(i)}catch(t){return i={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(i,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var i=[];return layui.each(e||o.chooseFiles,function(e,t){i.push(t.name)}),i}(),g={preview:function(e){o.preview(e)},upload:function(e,i){var t={};t[e]=i,o.upload(t)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,i){o.files[e]=i}),o.files},resetFile:function(e,i,t){var n=new File([i],t);o.files=o.files||{},o.files[e]=n}},y=function(){if("choose"!==t&&!l.auto||(l.choose&&l.choose(g),"choose"!==t))return l.before&&l.before(g),a.ie?a.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,i){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(i))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var i=0,t=e||o.files||o.chooseFiles||r.files;return layui.each(t,function(){i++}),i}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,i){if(i.size>1024*l.size){var t=l.size/1024;t=t>=1?t.toFixed(2)+"MB":l.size+"KB",r.value="",F=t}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,t=e.config,o=function(i){e.chooseFiles={},layui.each(i,function(i,t){var n=(new Date).getTime();e.chooseFiles[n+"-"+i]=t})},l=function(i,n){var a=e.elemFile,o=i.length>1?i.length+"个文件":(i[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||t.choose||a.after(''+o+"")};t.elem.off("upload.start").on("upload.start",function(){var a=i(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=i.extend({},t,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||t.elem.off("upload.over").on("upload.over",function(){var e=i(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=i(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=i(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),t.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var i=this.files||[];o(i),t.auto?e.upload():l(i)}),t.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),t.elem.data("haveEvents")||(e.elemFile.on("change",function(){i(this).trigger("upload.change")}),t.elem.on("click",function(){e.isFile()||i(this).trigger("upload.start")}),t.drag&&t.elem.on("dragover",function(e){e.preventDefault(),i(this).trigger("upload.over")}).on("dragleave",function(e){i(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),i(this).trigger("upload.drop",e)}),t.bindAction.on("click",function(){i(this).trigger("upload.action")}),t.elem.data("haveEvents",!0))},o.render=function(e){var i=new p(e);return l.call(i)},e(r,o)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/lay/modules/util.js b/agent-analyer/src/main/resources/static/js/layui/lay/modules/util.js new file mode 100644 index 0000000..cbfc0f0 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/lay/modules/util.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var t=layui.$,i={fixbar:function(e){var i,a,o="layui-fixbar",r="layui-fixbar-top",l=t(document),n=t("body");e=t.extend({showHeight:200},e),e.bar1=e.bar1===!0?"":e.bar1,e.bar2=e.bar2===!0?"":e.bar2,e.bgcolor=e.bgcolor?"background-color:"+e.bgcolor:"";var c=[e.bar1,e.bar2,""],g=t(['
        ',e.bar1?'
      • '+c[0]+"
      • ":"",e.bar2?'
      • '+c[1]+"
      • ":"",'
      • '+c[2]+"
      • ","
      "].join("")),u=g.find("."+r),s=function(){var t=l.scrollTop();t>=e.showHeight?i||(u.show(),i=1):i&&(u.hide(),i=0)};t("."+o)[0]||("object"==typeof e.css&&g.css(e.css),n.append(g),s(),g.find("li").on("click",function(){var i=t(this),a=i.attr("lay-type");"top"===a&&t("html,body").animate({scrollTop:0},200),e.click&&e.click.call(this,a)}),l.on("scroll",function(){clearTimeout(a),a=setTimeout(function(){s()},100)}))},countdown:function(e,t,i){var a=this,o="function"==typeof t,r=new Date(e).getTime(),l=new Date(!t||o?(new Date).getTime():t).getTime(),n=r-l,c=[Math.floor(n/864e5),Math.floor(n/36e5)%24,Math.floor(n/6e4)%60,Math.floor(n/1e3)%60];o&&(i=t);var g=setTimeout(function(){a.countdown(e,l+1e3,i)},1e3);return i&&i(n>0?c:[0,0,0,0],t,g),n<=0&&clearTimeout(g),g},timeAgo:function(e,t){var i=this,a=[[],[]],o=(new Date).getTime()-new Date(e).getTime();return o>6912e5?(o=new Date(e),a[0][0]=i.digit(o.getFullYear(),4),a[0][1]=i.digit(o.getMonth()+1),a[0][2]=i.digit(o.getDate()),t||(a[1][0]=i.digit(o.getHours()),a[1][1]=i.digit(o.getMinutes()),a[1][2]=i.digit(o.getSeconds())),a[0].join("-")+" "+a[1].join(":")):o>=864e5?(o/1e3/60/60/24|0)+"天前":o>=36e5?(o/1e3/60/60|0)+"小时前":o>=12e4?(o/1e3/60|0)+"分钟前":o<0?"未来":"刚刚"},digit:function(e,t){var i="";e=String(e),t=t||2;for(var a=e.length;a/g,">").replace(/'/g,"'").replace(/"/g,""")}};e("util",i)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/layui.all.js b/agent-analyer/src/main/resources/static/js/layui/layui.all.js new file mode 100644 index 0000000..2cbaf84 --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/layui.all.js @@ -0,0 +1,5 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;!function(e){"use strict";var t=document,n={modules:{},status:{},timeout:10,event:{}},o=function(){this.v="2.3.0"},r=function(){var e=t.currentScript?t.currentScript.src:function(){for(var e,n=t.scripts,o=n.length-1,r=o;r>0;r--)if("interactive"===n[r].readyState){e=n[r].src;break}return e||n[o].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),a=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},i="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",rate:"modules/rate",carousel:"modules/carousel",flow:"modules/flow",util:"modules/util",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};o.prototype.cache=n,o.prototype.define=function(e,t){var o=this,r="function"==typeof e,a=function(){var e=function(e,t){layui[e]=t,n.status[e]=!0};return"function"==typeof t&&t(function(o,r){e(o,r),n.callback[o]=function(){t(e)}}),this};return r&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?a.call(o):(o.use(e,a),o)},o.prototype.use=function(e,o,l){function s(e,t){var o="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||o.test((e.currentTarget||e.srcElement).readyState))&&(n.modules[d]=t,f.removeChild(v),function r(){return++m>1e3*n.timeout/4?a(d+" is not a valid module"):void(n.status[d]?c():setTimeout(r,4))}())}function c(){l.push(layui[d]),e.length>1?y.use(e.slice(1),o,l):"function"==typeof o&&o.apply(layui,l)}var y=this,p=n.dir=n.dir?n.dir:r,f=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,n){"jquery"===n&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var d=e[0],m=0;if(l=l||[],n.host=n.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[d]||!layui["layui.all"]&&layui["layui.mobile"]&&u[d])return c(),y;if(n.modules[d])!function g(){return++m>1e3*n.timeout/4?a(d+" is not a valid module"):void("string"==typeof n.modules[d]&&n.status[d]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[d]?p+"lay/":/^\{\/\}/.test(y.modules[d])?"":n.base||"")+(y.modules[d]||d)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=n.version===!0?n.v||(new Date).getTime():n.version||"";return e?"?v="+e:""}(),f.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||i?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),n.modules[d]=h}return y},o.prototype.getStyle=function(t,n){var o=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return o[o.getPropertyValue?"getPropertyValue":"getAttribute"](n)},o.prototype.link=function(e,o,r){var i=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof o&&(r=o);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(n.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof o?i:(function p(){return++y>1e3*n.timeout/100?a(e+" timeout"):void(1989===parseInt(i.getStyle(t.getElementById(c),"width"))?function(){o()}():setTimeout(p,100))}(),i)},n.callback={},o.prototype.factory=function(e){if(layui[e])return"function"==typeof n.callback[e]?n.callback[e]:null},o.prototype.addcss=function(e,t,o){return layui.link(n.dir+"css/"+e,t,o)},o.prototype.img=function(e,t,n){var o=new Image;return o.src=e,o.complete?t(o):(o.onload=function(){o.onload=null,"function"==typeof t&&t(o)},void(o.onerror=function(e){o.onerror=null,"function"==typeof n&&n(e)}))},o.prototype.config=function(e){e=e||{};for(var t in e)n[t]=e[t];return this},o.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),o.prototype.extend=function(e){var t=this;e=e||{};for(var n in e)t[n]||t.modules[n]?a("模块名 "+n+" 已被占用"):t.modules[n]=e[n];return t},o.prototype.router=function(e){var t=this,e=e||location.hash,n={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),n.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),n.search[t[0]]=t[1]}():n.path.push(t)}),n):n},o.prototype.data=function(t,n,o){if(t=t||"layui",o=o||localStorage,e.JSON&&e.JSON.parse){if(null===n)return delete o[t];n="object"==typeof n?n:{key:n};try{var r=JSON.parse(o[t])}catch(a){var r={}}return"value"in n&&(r[n.key]=n.value),n.remove&&delete r[n.key],o[t]=JSON.stringify(r),n.key?r[n.key]:r}},o.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},o.prototype.device=function(t){var n=navigator.userAgent.toLowerCase(),o=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(n.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(n)?"windows":/linux/.test(n)?"linux":/iphone|ipod|ipad|ios/.test(n)?"ios":/mac/.test(n)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((n.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:o("micromessenger")};return t&&!r[t]&&(r[t]=o(t)),r.android=/android/.test(n),r.ios="ios"===r.os,r},o.prototype.hint=function(){return{error:a}},o.prototype.each=function(e,t){var n,o=this;if("function"!=typeof t)return o;if(e=e||[],e.constructor===Object){for(n in e)if(t.call(e[n],n,e[n]))break}else for(n=0;na?1:r/g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});layui.define(function(e){"use strict";var a=document,t="getElementById",n="getElementsByTagName",i="laypage",r="layui-disabled",u=function(e){var a=this;a.config=e||{},a.config.index=++s.index,a.render(!0)};u.prototype.type=function(){var e=this.config;if("object"==typeof e.elem)return void 0===e.elem.length?2:3},u.prototype.view=function(){var e=this,a=e.config,t=a.groups="groups"in a?0|a.groups:5;a.layout="object"==typeof a.layout?a.layout:["prev","page","next"],a.count=0|a.count,a.curr=0|a.curr||1,a.limits="object"==typeof a.limits?a.limits:[10,20,30,40,50],a.limit=0|a.limit||10,a.pages=Math.ceil(a.count/a.limit)||1,a.curr>a.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+"":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+"");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('');r<=u;r++)r===a.curr?e.push('"+r+""):e.push(''+r+"");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+"")),e.join("")}(),next:function(){return a.next?''+a.next+"":""}(),count:'共 '+a.count+" 条",limit:function(){var e=['"}(),refresh:['','',""].join(""),skip:function(){return['到第','','页',""].join("")}()};return['
      ',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
      "].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)});!function(){"use strict";var e=window.layui&&layui.define,t={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,n=t.length-1,a=n;a>0;a--)if("interactive"===t[a].readyState){e=t[a].src;break}return e||t[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),getStyle:function(e,t){var n=e.currentStyle?e.currentStyle:window.getComputedStyle(e,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](t)},link:function(e,a,i){if(n.path){var r=document.getElementsByTagName("head")[0],o=document.createElement("link");"string"==typeof a&&(i=a);var s=(i||e).replace(/\.|\//g,""),l="layuicss-"+s,d=0;o.rel="stylesheet",o.href=n.path+e,o.id=l,document.getElementById(l)||r.appendChild(o),"function"==typeof a&&!function c(){return++d>80?window.console&&console.error("laydate.css: Invalid"):void(1989===parseInt(t.getStyle(document.getElementById(l),"width"))?a():setTimeout(c,100))}()}}},n={v:"5.0.9",config:{},index:window.laydate&&window.laydate.v?1e5:0,path:t.getPath,set:function(e){var t=this;return t.config=w.extend({},t.config,e),t},ready:function(a){var i="laydate",r="",o=(e?"modules/laydate/":"theme/")+"default/laydate.css?v="+n.v+r;return e?layui.addcss(o,a,i):t.link(o,a,i),this}},a=function(){var e=this;return{hint:function(t){e.hint.call(e,t)},config:e.config}},i="laydate",r=".layui-laydate",o="layui-this",s="laydate-disabled",l="开始日期超出了结束日期
      建议重新选择",d=[100,2e5],c="layui-laydate-static",m="layui-laydate-list",u="laydate-selected",h="layui-laydate-hint",y="laydate-day-prev",f="laydate-day-next",p="layui-laydate-footer",g=".laydate-btns-confirm",v="laydate-time-text",D=".laydate-btns-time",T=function(e){var t=this;t.index=++n.index,t.config=w.extend({},t.config,n.config,e),n.ready(function(){t.init()})},w=function(e){return new C(e)},C=function(e){for(var t=0,n="object"==typeof e?[e]:(this.selector=e,document.querySelectorAll(e||null));t0)return n[0].getAttribute(e)}():n.each(function(n,a){a.setAttribute(e,t)})},C.prototype.removeAttr=function(e){return this.each(function(t,n){n.removeAttribute(e)})},C.prototype.html=function(e){return this.each(function(t,n){n.innerHTML=e})},C.prototype.val=function(e){return this.each(function(t,n){n.value=e})},C.prototype.append=function(e){return this.each(function(t,n){"object"==typeof e?n.appendChild(e):n.innerHTML=n.innerHTML+e})},C.prototype.remove=function(e){return this.each(function(t,n){e?n.removeChild(e):n.parentNode.removeChild(n)})},C.prototype.on=function(e,t){return this.each(function(n,a){a.attachEvent?a.attachEvent("on"+e,function(e){e.target=e.srcElement,t.call(a,e)}):a.addEventListener(e,t,!1)})},C.prototype.off=function(e,t){return this.each(function(n,a){a.detachEvent?a.detachEvent("on"+e,t):a.removeEventListener(e,t,!1)})},T.isLeapYear=function(e){return e%4===0&&e%100!==0||e%400===0},T.prototype.config={type:"date",range:!1,format:"yyyy-MM-dd",value:null,isInitValue:!0,min:"1900-1-1",max:"2099-12-31",trigger:"focus",show:!1,showBottom:!0,btns:["clear","now","confirm"],lang:"cn",theme:"default",position:null,calendar:!1,mark:{},zIndex:null,done:null,change:null},T.prototype.lang=function(){var e=this,t=e.config,n={cn:{weeks:["日","一","二","三","四","五","六"],time:["时","分","秒"],timeTips:"选择时间",startTime:"开始时间",endTime:"结束时间",dateTips:"返回日期",month:["一","二","三","四","五","六","七","八","九","十","十一","十二"],tools:{confirm:"确定",clear:"清空",now:"现在"}},en:{weeks:["Su","Mo","Tu","We","Th","Fr","Sa"],time:["Hours","Minutes","Seconds"],timeTips:"Select Time",startTime:"Start Time",endTime:"End Time",dateTips:"Select Date",month:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],tools:{confirm:"Confirm",clear:"Clear",now:"Now"}}};return n[t.lang]||n.cn},T.prototype.init=function(){var e=this,t=e.config,n="yyyy|y|MM|M|dd|d|HH|H|mm|m|ss|s",a="static"===t.position,i={year:"yyyy",month:"yyyy-MM",date:"yyyy-MM-dd",time:"HH:mm:ss",datetime:"yyyy-MM-dd HH:mm:ss"};t.elem=w(t.elem),t.eventElem=w(t.eventElem),t.elem[0]&&(t.range===!0&&(t.range="-"),t.format===i.date&&(t.format=i[t.type]),e.format=t.format.match(new RegExp(n+"|.","g"))||[],e.EXP_IF="",e.EXP_SPLIT="",w.each(e.format,function(t,a){var i=new RegExp(n).test(a)?"\\d{"+function(){return new RegExp(n).test(e.format[0===t?t+1:t-1]||"")?/^yyyy|y$/.test(a)?4:a.length:/^yyyy$/.test(a)?"1,4":/^y$/.test(a)?"1,308":"1,2"}()+"}":"\\"+a;e.EXP_IF=e.EXP_IF+i,e.EXP_SPLIT=e.EXP_SPLIT+"("+i+")"}),e.EXP_IF=new RegExp("^"+(t.range?e.EXP_IF+"\\s\\"+t.range+"\\s"+e.EXP_IF:e.EXP_IF)+"$"),e.EXP_SPLIT=new RegExp("^"+e.EXP_SPLIT+"$",""),e.isInput(t.elem[0])||"focus"===t.trigger&&(t.trigger="click"),t.elem.attr("lay-key")||(t.elem.attr("lay-key",e.index),t.eventElem.attr("lay-key",e.index)),t.mark=w.extend({},t.calendar&&"cn"===t.lang?{"0-1-1":"元旦","0-2-14":"情人","0-3-8":"妇女","0-3-12":"植树","0-4-1":"愚人","0-5-1":"劳动","0-5-4":"青年","0-6-1":"儿童","0-9-10":"教师","0-9-18":"国耻","0-10-1":"国庆","0-12-25":"圣诞"}:{},t.mark),w.each(["min","max"],function(e,n){var a=[],i=[];if("number"==typeof t[n]){var r=t[n],o=(new Date).getTime(),s=864e5,l=new Date(r?r0)return!0;var a=w.elem("div",{"class":"layui-laydate-header"}),i=[function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-y"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-m"});return e.innerHTML="",e}(),function(){var e=w.elem("div",{"class":"laydate-set-ym"}),t=w.elem("span"),n=w.elem("span");return e.appendChild(t),e.appendChild(n),e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-m"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-y"});return e.innerHTML="",e}()],d=w.elem("div",{"class":"layui-laydate-content"}),c=w.elem("table"),m=w.elem("thead"),u=w.elem("tr");w.each(i,function(e,t){a.appendChild(t)}),m.appendChild(u),w.each(new Array(6),function(e){var t=c.insertRow(0);w.each(new Array(7),function(a){if(0===e){var i=w.elem("th");i.innerHTML=n.weeks[a],u.appendChild(i)}t.insertCell(a)})}),c.insertBefore(m,c.children[0]),d.appendChild(c),r[e]=w.elem("div",{"class":"layui-laydate-main laydate-main-list-"+e}),r[e].appendChild(a),r[e].appendChild(d),o.push(i),s.push(d),l.push(c)}),w(d).html(function(){var e=[],i=[];return"datetime"===t.type&&e.push(''+n.timeTips+""),w.each(t.btns,function(e,r){var o=n.tools[r]||"btn";t.range&&"now"===r||(a&&"clear"===r&&(o="cn"===t.lang?"重置":"Reset"),i.push(''+o+""))}),e.push('"),e.join("")}()),w.each(r,function(e,t){i.appendChild(t)}),t.showBottom&&i.appendChild(d),/^#/.test(t.theme)){var m=w.elem("style"),u=["#{{id}} .layui-laydate-header{background-color:{{theme}};}","#{{id}} .layui-this{background-color:{{theme}} !important;}"].join("").replace(/{{id}}/g,e.elemID).replace(/{{theme}}/g,t.theme);"styleSheet"in m?(m.setAttribute("type","text/css"),m.styleSheet.cssText=u):m.innerHTML=u,w(i).addClass("laydate-theme-molv"),i.appendChild(m)}e.remove(T.thisElemDate),a?t.elem.append(i):(document.body.appendChild(i),e.position()),e.checkDate().calendar(),e.changeEvent(),T.thisElemDate=e.elemID,"function"==typeof t.ready&&t.ready(w.extend({},t.dateTime,{month:t.dateTime.month+1}))},T.prototype.remove=function(e){var t=this,n=(t.config,w("#"+(e||t.elemID)));return n.hasClass(c)||t.checkDate(function(){n.remove()}),t},T.prototype.position=function(){var e=this,t=e.config,n=e.bindElem||t.elem[0],a=n.getBoundingClientRect(),i=e.elem.offsetWidth,r=e.elem.offsetHeight,o=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},s=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},l=5,d=a.left,c=a.bottom;d+i+l>s("width")&&(d=s("width")-i-l),c+r+l>s()&&(c=a.top>r?a.top-r:s()-r,c-=2*l),t.position&&(e.elem.style.position=t.position),e.elem.style.left=d+("fixed"===t.position?0:o(1))+"px",e.elem.style.top=c+("fixed"===t.position?0:o())+"px"},T.prototype.hint=function(e){var t=this,n=(t.config,w.elem("div",{"class":h}));n.innerHTML=e||"",w(t.elem).find("."+h).remove(),t.elem.appendChild(n),clearTimeout(t.hinTimer),t.hinTimer=setTimeout(function(){w(t.elem).find("."+h).remove()},3e3)},T.prototype.getAsYM=function(e,t,n){return n?t--:t++,t<0&&(t=11,e--),t>11&&(t=0,e++),[e,t]},T.prototype.systemDate=function(e){var t=e||new Date;return{year:t.getFullYear(),month:t.getMonth(),date:t.getDate(),hours:e?e.getHours():0,minutes:e?e.getMinutes():0,seconds:e?e.getSeconds():0}},T.prototype.checkDate=function(e){var t,a,i=this,r=(new Date,i.config),o=r.dateTime=r.dateTime||i.systemDate(),s=i.bindElem||r.elem[0],l=(i.isInput(s)?"val":"html",i.isInput(s)?s.value:"static"===r.position?"":s.innerHTML),c=function(e){e.year>d[1]&&(e.year=d[1],a=!0),e.month>11&&(e.month=11,a=!0),e.hours>23&&(e.hours=0,a=!0),e.minutes>59&&(e.minutes=0,e.hours++,a=!0),e.seconds>59&&(e.seconds=0,e.minutes++,a=!0),t=n.getEndDate(e.month+1,e.year),e.date>t&&(e.date=t,a=!0)},m=function(e,t,n){var o=["startTime","endTime"];t=(t.match(i.EXP_SPLIT)||[]).slice(1),n=n||0,r.range&&(i[o[n]]=i[o[n]]||{}),w.each(i.format,function(s,l){var c=parseFloat(t[s]);t[s].length必须遵循下述格式:
      "+(r.range?r.format+" "+r.range+" "+r.format:r.format)+"
      已为你重置"),a=!0):l&&l.constructor===Date?r.dateTime=i.systemDate(l):(r.dateTime=i.systemDate(),delete i.startState,delete i.endState,delete i.startDate,delete i.endDate,delete i.startTime,delete i.endTime),c(o),a&&l&&i.setValue(r.range?i.endDate?i.parse():"":i.parse()),e&&e(),i)},T.prototype.mark=function(e,t){var n,a=this,i=a.config;return w.each(i.mark,function(e,a){var i=e.split("-");i[0]!=t[0]&&0!=i[0]||i[1]!=t[1]&&0!=i[1]||i[2]!=t[2]||(n=a||t[2])}),n&&e.html(''+n+""),a},T.prototype.limit=function(e,t,n,a){var i,r=this,o=r.config,l={},d=o[n>41?"endDate":"dateTime"],c=w.extend({},d,t||{});return w.each({now:c,min:o.min,max:o.max},function(e,t){l[e]=r.newDate(w.extend({year:t.year,month:t.month,date:t.date},function(){var e={};return w.each(a,function(n,a){e[a]=t[a]}),e}())).getTime()}),i=l.nowl.max,e&&e[i?"addClass":"removeClass"](s),i},T.prototype.calendar=function(e){var t,a,i,r=this,s=r.config,l=e||s.dateTime,c=new Date,m=r.lang(),u="date"!==s.type&&"datetime"!==s.type,h=e?1:0,y=w(r.table[h]).find("td"),f=w(r.elemHeader[h][2]).find("span");if(l.yeard[1]&&(l.year=d[1],r.hint("最高只能支持到公元"+d[1]+"年")),r.firstDate||(r.firstDate=w.extend({},l)),c.setFullYear(l.year,l.month,1),t=c.getDay(),a=n.getEndDate(l.month||12,l.year),i=n.getEndDate(l.month+1,l.year),w.each(y,function(e,n){var d=[l.year,l.month],c=0;n=w(n),n.removeAttr("class"),e=t&&e=n.firstDate.year&&(r.month=a.max.month,r.date=a.max.date),n.limit(w(i),r,t),M++}),w(u[f?0:1]).attr("lay-ym",M-8+"-"+T[1]).html(b+p+" - "+(M-1+p))}else if("month"===e)w.each(new Array(12),function(e){var i=w.elem("li",{"lay-ym":e}),s={year:T[0],month:e};e+1==T[1]&&w(i).addClass(o),i.innerHTML=r.month[e]+(f?"月":""),d.appendChild(i),T[0]=n.firstDate.year&&(s.date=a.max.date),n.limit(w(i),s,t)}),w(u[f?0:1]).attr("lay-ym",T[0]+"-"+T[1]).html(T[0]+p);else if("time"===e){var E=function(){w(d).find("ol").each(function(e,a){w(a).find("li").each(function(a,i){n.limit(w(i),[{hours:a},{hours:n[x].hours,minutes:a},{hours:n[x].hours,minutes:n[x].minutes,seconds:a}][e],t,[["hours"],["hours","minutes"],["hours","minutes","seconds"]][e])})}),a.range||n.limit(w(n.footer).find(g),n[x],0,["hours","minutes","seconds"])};a.range?n[x]||(n[x]={hours:0,minutes:0,seconds:0}):n[x]=i,w.each([24,60,60],function(e,t){var a=w.elem("li"),i=["

      "+r.time[e]+"

        "];w.each(new Array(t),function(t){i.push(""+w.digit(t,2)+"")}),a.innerHTML=i.join("")+"
      ",d.appendChild(a)}),E()}if(y&&h.removeChild(y),h.appendChild(d),"year"===e||"month"===e)w(n.elemMain[t]).addClass("laydate-ym-show"),w(d).find("li").on("click",function(){var r=0|w(this).attr("lay-ym");if(!w(this).hasClass(s)){if(0===t)i[e]=r,l&&(n.startDate[e]=r),n.limit(w(n.footer).find(g),null,0);else if(l)n.endDate[e]=r;else{var c="year"===e?n.getAsYM(r,T[1]-1,"sub"):n.getAsYM(T[0],r,"sub");w.extend(i,{year:c[0],month:c[1]})}"year"===a.type||"month"===a.type?(w(d).find("."+o).removeClass(o),w(this).addClass(o),"month"===a.type&&"year"===e&&(n.listYM[t][0]=r,l&&(n[["startDate","endDate"][t]].year=r),n.list("month",t))):(n.checkDate("limit").calendar(),n.closeList()),n.setBtnStatus(),a.range||n.done(null,"change"),w(n.footer).find(D).removeClass(s)}});else{var S=w.elem("span",{"class":v}),k=function(){w(d).find("ol").each(function(e){var t=this,a=w(t).find("li");t.scrollTop=30*(n[x][C[e]]-2),t.scrollTop<=0&&a.each(function(e,n){if(!w(this).hasClass(s))return t.scrollTop=30*(e-2),!0})})},H=w(c[2]).find("."+v);k(),S.innerHTML=a.range?[r.startTime,r.endTime][t]:r.timeTips,w(n.elemMain[t]).addClass("laydate-time-show"),H[0]&&H.remove(),c[2].appendChild(S),w(d).find("ol").each(function(e){var t=this;w(t).find("li").on("click",function(){var r=0|this.innerHTML;w(this).hasClass(s)||(a.range?n[x][C[e]]=r:i[C[e]]=r,w(t).find("."+o).removeClass(o),w(this).addClass(o),E(),k(),(n.endDate||"time"===a.type)&&n.done(null,"change"),n.setBtnStatus())})})}return n},T.prototype.listYM=[],T.prototype.closeList=function(){var e=this;e.config;w.each(e.elemCont,function(t,n){w(this).find("."+m).remove(),w(e.elemMain[t]).removeClass("laydate-ym-show laydate-time-show")}),w(e.elem).find("."+v).remove()},T.prototype.setBtnStatus=function(e,t,n){var a,i=this,r=i.config,o=w(i.footer).find(g),d=r.range&&"date"!==r.type&&"time"!==r.type;d&&(t=t||i.startDate,n=n||i.endDate,a=i.newDate(t).getTime()>i.newDate(n).getTime(),i.limit(null,t)||i.limit(null,n)?o.addClass(s):o[a?"addClass":"removeClass"](s),e&&a&&i.hint("string"==typeof e?l.replace(/日期/g,e):l))},T.prototype.parse=function(e,t){var n=this,a=n.config,i=t||(e?w.extend({},n.endDate,n.endTime):a.range?w.extend({},n.startDate,n.startTime):a.dateTime),r=n.format.concat();return w.each(r,function(e,t){/yyyy|y/.test(t)?r[e]=w.digit(i.year,t.length):/MM|M/.test(t)?r[e]=w.digit(i.month+1,t.length):/dd|d/.test(t)?r[e]=w.digit(i.date,t.length):/HH|H/.test(t)?r[e]=w.digit(i.hours,t.length):/mm|m/.test(t)?r[e]=w.digit(i.minutes,t.length):/ss|s/.test(t)&&(r[e]=w.digit(i.seconds,t.length))}),a.range&&!e?r.join("")+" "+a.range+" "+n.parse(1):r.join("")},T.prototype.newDate=function(e){return e=e||{},new Date(e.year||1,e.month||0,e.date||1,e.hours||0,e.minutes||0,e.seconds||0)},T.prototype.setValue=function(e){var t=this,n=t.config,a=t.bindElem||n.elem[0],i=t.isInput(a)?"val":"html";return"static"===n.position||w(a)[i](e||""),this},T.prototype.stampRange=function(){var e,t,n=this,a=n.config,i=w(n.elem).find("td");if(a.range&&!n.endDate&&w(n.footer).find(g).addClass(s),n.endDate)return e=n.newDate({year:n.startDate.year,month:n.startDate.month,date:n.startDate.date}).getTime(),t=n.newDate({year:n.endDate.year,month:n.endDate.month,date:n.endDate.date}).getTime(),e>t?n.hint(l):void w.each(i,function(a,i){var r=w(i).attr("lay-ymd").split("-"),s=n.newDate({year:r[0],month:r[1]-1,date:r[2]}).getTime();w(i).removeClass(u+" "+o),s!==e&&s!==t||w(i).addClass(w(i).hasClass(y)||w(i).hasClass(f)?u:o),s>e&&s0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(!fe.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ge,"ms-").replace(me,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var r,i=0;if(n(e))for(r=e.length;iT.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[P]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function f(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;i-1&&(r[l]=!(a[l]=f))}}else x=m(x===a?x.splice(h,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s1&&h(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(r,a,s,u,l){var c,f,d,p=0,h="0",g=r&&[],y=[],v=A,x=r||o&&T.find.TAG("*",l),b=W+=null==v?1:Math.random()||.1,w=x.length;for(l&&(A=a===H||a||l);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===H||(L(c),s=!_);d=e[f++];)if(d(c,a||H,s)){u.push(c);break}l&&(W=b)}i&&((c=!d&&c)&&p--,r&&g.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(g,y,a,s);if(r){if(p>0)for(;h--;)g[h]||y[h]||(y[h]=G.call(u));y=m(y)}Q.apply(u,y),l&&!r&&y.length>0&&p+n.length>1&&t.uniqueSort(u)}return l&&(W=b,A=v),g};return i?r(a):a}var b,w,T,C,E,N,k,S,A,D,j,L,H,q,_,F,M,O,R,P="sizzle"+1*new Date,B=e.document,W=0,I=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(j=!0),0},V=1<<31,Y={}.hasOwnProperty,J=[],G=J.pop,K=J.push,Q=J.push,Z=J.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,xe=/'|\\/g,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Te=function(){L()};try{Q.apply(J=Z.call(B.childNodes),B.childNodes),J[B.childNodes.length].nodeType}catch(Ce){Q={apply:J.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:B;return r!==H&&9===r.nodeType&&r.documentElement?(H=r,q=H.documentElement,_=!E(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=me.test(H.getElementsByClassName),w.getById=i(function(e){return q.appendChild(e).id=P,!H.getElementsByName||!H.getElementsByName(P).length}),w.getById?(T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&_){var n=t.getElementById(e);return n?[n]:[]}},T.filter.ID=function(e){var t=e.replace(be,we);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(be,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&_)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=me.test(H.querySelectorAll))&&(i(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+P+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+P+"+*").length||F.push(".#.+[+~]")}),i(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=me.test(O=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=me.test(q.compareDocumentPosition),R=t||me.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===B&&R(B,e)?-1:t===H||t.ownerDocument===B&&R(B,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===H?-1:t===H?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===B?-1:u[r]===B?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&_&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!_):void 0;return void 0!==r?r:w.attributes||!_?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!w.detectDuplicates,D=!w.sortStable&&e.slice(0),e.sort(U),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},C=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,we),e[3]=(e[3]||e[4]||e[5]||"").replace(be,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s,x=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(d=m,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}), +l=c[e]||[],p=l[0]===W&&l[1],x=p&&l[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[W,p,x];break}}else if(v&&(d=t,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),l=c[e]||[],p=l[0]===W&&l[1],x=p),x===!1)for(;(d=++p&&d&&d[g]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==y:1!==d.nodeType)||!++x||(v&&(f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),c[e]=[W,x]),d!==t)););return x-=i,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[P]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=ee(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(se,"$1"));return i[P]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,we),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,we).toLowerCase(),function(t){var n;do if(n=_?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&_&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(be,we),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((u=T.find[s])&&(r=u(a.matches[0].replace(be,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Q.apply(n,r),n;break}}return(l||k(e,f))(r,t,!_,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=P.split("").sort(U).join("")===P,w.detectDuplicates=!!j,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var xe=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&pe(e).is(n))break;r.push(e)}return r},be=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Te=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Ce=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?pe.find.matchesSelector(r,e)?[r]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Ee,Ne=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ee,"string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ne.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:re,!0)),Te.test(r[1])&&pe.isPlainObject(t))for(r in t)pe.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}if(i=re.getElementById(r[2]),i&&i.parentNode){if(i.id!==r[2])return Ee.find(e);this.length=1,this[0]=i}return this.context=re,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Ee=pe(re);var Se=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),r=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return xe(e,"parentNode")},parentsUntil:function(e,t,n){return xe(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return xe(e,"nextSibling")},prevAll:function(e){return xe(e,"previousSibling")},nextUntil:function(e,t,n){return xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return xe(e,"previousSibling",n)},siblings:function(e){return be((e.parentNode||{}).firstChild,e)},children:function(e){return be(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,r){var i=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=pe.filter(r,i)),this.length>1&&(Ae[e]||(i=pe.uniqueSort(i)),Se.test(e)&&(i=i.reverse())),this.pushStack(i)}});var De=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,r,i,a=[],s=[],u=-1,l=function(){for(i=e.once,r=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),n<=u&&u--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=!0,n||c.disable(),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,r):r}},i={};return r.pipe=r.then,pe.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=ie.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,u=1===s?e:pe.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?ie.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);i0||(je.resolveWith(re,[pe]),pe.fn.triggerHandler&&(pe(re).triggerHandler("ready"),pe(re).off("ready"))))}}),pe.ready.promise=function(t){if(!je)if(je=pe.Deferred(),"complete"===re.readyState||"loading"!==re.readyState&&!re.documentElement.doScroll)e.setTimeout(pe.ready);else if(re.addEventListener)re.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{re.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&re.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!pe.isReady){try{n.doScroll("left")}catch(t){return e.setTimeout(i,50)}a(),pe.ready()}}()}return je.promise(t)},pe.ready.promise();var Le;for(Le in pe(fe))break;fe.ownFirst="0"===Le,fe.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,r;n=re.getElementsByTagName("body")[0],n&&n.style&&(t=re.createElement("div"),r=re.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",fe.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=re.createElement("div");fe.deleteExpando=!0;try{delete e.test}catch(t){fe.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,_e=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!l(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return f(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return f(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=pe.camelCase(r.slice(5)),u(o,r,i[r])));pe._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?u(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=pe._data(e,t),n&&(!r||pe.isArray(n)?r=pe._data(e,t,pe.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),r=n.length,i=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
      a",fe.leadingWhitespace=3===e.firstChild.nodeType,fe.tbody=!e.getElementsByTagName("tbody").length,fe.htmlSerialize=!!e.getElementsByTagName("link").length,fe.html5Clone="<:nav>"!==re.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),fe.appendChecked=n.checked,e.innerHTML="",fe.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=re.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),fe.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,fe.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
      ","
      "],area:[1,"",""],param:[1,"",""],thead:[1,"","
      "],tr:[2,"","
      "],col:[2,"","
      "],td:[3,"","
      "],_default:fe.htmlSerialize?[0,"",""]:[1,"X
      ","
      "]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:pe.makeArray(n,[t]),l=pe.event.special[p]||{},i||!l.trigger||l.trigger.apply(r,n)!==!1)){if(!i&&!l.noBubble&&!pe.isWindow(r)){for(u=l.delegateType||p,Ke.test(u+p)||(s=s.parentNode);s;s=s.parentNode)d.push(s),c=s;c===(r.ownerDocument||re)&&d.push(c.defaultView||c.parentWindow||e)}for(f=0;(s=d[f++])&&!t.isPropagationStopped();)t.type=f>1?u:l.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!l._default||l._default.apply(d.pop(),n)===!1)&&He(r)&&a&&r[p]&&!pe.isWindow(r)){c=r[a],c&&(r[a]=null),pe.event.triggered=p;try{r[p]()}catch(g){}pe.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,r,i,o,a=[],s=ie.call(arguments),u=(pe._data(this,"events")||{})[e.type]||[],l=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,u),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,r=((pe.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,u=e.target;if(s&&u.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(r=[],n=0;n-1:pe.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&a.push({elem:u,handlers:r})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(re),st=at.appendChild(re.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var r,i,o,a,s,u=pe.contains(e.ownerDocument,e);if(fe.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(fe.noCloneEvent&&fe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(r=h(o),s=h(e),a=0;null!=(i=s[a]);++a)r[a]&&k(i,r[a]);if(t)if(n)for(s=s||h(e),r=r||h(o),a=0;null!=(i=s[a]);a++)N(i,r[a]);else N(e,o);return r=h(o,"script"),r.length>0&&g(r,!u&&h(e,"script")),r=s=i=null,o},cleanData:function(e,t){for(var n,r,i,o,a=0,s=pe.expando,u=pe.cache,l=fe.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(i=n[s],o=i&&u[i])){if(o.events)for(r in o.events)c[r]?pe.event.remove(n,r):pe.removeEvent(n,r,o.handle);u[i]&&(delete u[i],l||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(i))}}}),pe.fn.extend({domManip:S,detach:function(e){return A(this,e,!0)},remove:function(e){return A(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||re).createTextNode(e))},null,e,arguments.length)},append:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.appendChild(e)}})},prepend:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(fe.htmlSerialize||!et.test(e))&&(fe.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=l.getElementsByTagName("td"),t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),f.removeChild(u)}var n,r,i,o,a,s,u=re.createElement("div"),l=re.createElement("div");l.style&&(l.style.cssText="float:left;opacity:.5",fe.opacity="0.5"===l.style.opacity,fe.cssFloat=!!l.style.cssFloat,l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",fe.clearCloneStyle="content-box"===l.style.backgroundClip,u=re.createElement("div"),u.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",l.innerHTML="",u.appendChild(l),fe.boxSizing=""===l.style.boxSizing||""===l.style.MozBoxSizing||""===l.style.WebkitBoxSizing,pe.extend(fe,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),i},pixelMarginRight:function(){return null==n&&t(),r},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,gt,mt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!fe.pixelMarginRight()&&ft.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),ft.test(a)&&!mt.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,xt=/^(none|table(?!-c[ea]).+)/,bt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Tt={letterSpacing:"0",fontWeight:"400"},Ct=["Webkit","O","Moz","ms"],Et=re.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=gt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":fe.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=pe.camelCase(t),u=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];if(o=typeof n,"string"===o&&(i=Me.exec(n))&&i[1]&&(n=d(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(pe.cssNumber[s]?"":"px")),fe.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{u[t]=n}catch(l){}}},css:function(e,t,n,r){var i,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=gt(e,t,r)),"normal"===o&&t in Tt&&(o=Tt[t]),""===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,r){if(n)return xt.test(pe.css(e,"display"))&&0===e.offsetWidth?dt(e,wt,function(){return M(e,t,r)}):M(e,t,r)},set:function(e,n,r){var i=r&&ht(e);return _(e,n,r?F(e,t,r,fe.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,i),i):0)}}}),fe.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=yt.test(o)?o.replace(yt,i):o+" "+i)}}),pe.cssHooks.marginRight=L(fe.reliableMarginRight,function(e,t){if(t)return dt(e,{display:"inline-block"},gt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(fe.reliableMarginLeft,function(e,t){if(t)return(parseFloat(gt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-dt(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Oe[r]+t]=o[r]||o[r-2]||o[0];return i}},ct.test(e)||(pe.cssHooks[e+t].set=_)}),pe.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(pe.isArray(t)){for(r=ht(e),i=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Re(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=O.prototype.init,pe.fx.step={};var Nt,kt,St=/^(?:toggle|show|hide)$/,At=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(De);for(var n,r=0,i=e.length;r
      a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",fe.getSetAttribute="t"!==n.className,fe.style=/top/.test(e.getAttribute("style")),fe.hrefNormalized="/a"===e.getAttribute("href"),fe.checkOn=!!t.value,fe.optSelected=i.selected,fe.enctype=!!re.createElement("form").enctype,r.disabled=!0,fe.optDisabled=!i.disabled,t=re.createElement("input"),t.setAttribute("value",""),fe.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),fe.radioValue="t"===t.value}();var Dt=/\r/g,jt=/[\x20\t\r\n\f]+/g;pe.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=pe.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,pe(this).val()):e,null==i?i="":"number"==typeof i?i+="":pe.isArray(i)&&(i=pe.map(i,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=pe.valHooks[i.type]||pe.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(Dt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e)).replace(jt," ")}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,a=o?null:[],s=o?i+1:r.length,u=i<0?s:o?i:0;u-1)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},fe.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Lt,Ht,qt=pe.expr.attrHandle,_t=/^(?:checked|selected)$/i,Ft=fe.getSetAttribute,Mt=fe.input;pe.fn.extend({attr:function(e,t){return Pe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),i=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Ht:Lt)),void 0!==n?null===n?void pe.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=pe.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!fe.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(De);if(o&&1===e.nodeType)for(;n=o[i++];)r=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Mt&&Ft||!_t.test(n)?e[r]=!1:e[pe.camelCase("default-"+n)]=e[r]=!1:pe.attr(e,n,""),e.removeAttribute(Ft?n:r)}}),Ht={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Mt&&Ft||!_t.test(n)?e.setAttribute(!Ft&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=qt[t]||pe.find.attr;Mt&&Ft||!_t.test(t)?qt[t]=function(e,t,r){var i,o;return r||(o=qt[t],qt[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,qt[t]=o),i}:qt[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Mt&&Ft||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Lt&&Lt.set(e,t,n)}}),Ft||(Lt={set:function(e,t,n){var r=e.getAttributeNode(n);if(r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n))return t}},qt.id=qt.name=qt.coords=function(e,t,n){var r;if(!n)return(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Lt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Lt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),fe.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Ot=/^(?:input|select|textarea|button|object)$/i,Rt=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Pe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),pe.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,i=pe.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Ot.test(e.nodeName)||Rt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),fe.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),fe.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),fe.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=pe(this),o=e.match(De)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Bt=e.location,Wt=pe.now(),It=/\?/,$t=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=pe.trim(t+"");return i&&!pe.trim(i.replace($t,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new e.DOMParser,n=r.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var zt=/#.*$/,Xt=/([?&])_=[^&]*/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Vt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Yt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Kt={},Qt={},Zt="*/".concat("*"),en=Bt.href,tn=Gt.exec(en.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:en,type:"GET",isLocal:Vt.test(tn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Kt),ajaxTransport:X(Qt),ajax:function(t,n){function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c=void 0,s=i||"",T.readyState=t>0?4:0,o=t>=200&&t<300||304===t,r&&(x=Y(d,T,r)),x=J(d,x,T,o),o?(d.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=T.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===d.type?C="nocontent":304===t?C="notmodified":(C=x.state,f=x.data,v=x.error,o=!v)):(v=C,!t&&C||(C="error",t<0&&(t=0))),T.status=t,T.statusText=(n||C)+"",o?g.resolveWith(p,[f,C,T]):g.rejectWith(p,[T,C,v]),T.statusCode(y),y=void 0,l&&h.trigger(o?"ajaxSuccess":"ajaxError",[T,d,o?f:v]),m.fireWith(p,[T,C]),l&&(h.trigger("ajaxComplete",[T,d]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,d=pe.ajaxSetup({},n),p=d.context||d,h=d.context&&(p.nodeType||p.jquery)?pe(p):pe.event,g=pe.Deferred(),m=pe.Callbacks("once memory"),y=d.statusCode||{},v={},x={},b=0,w="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!f)for(f={};t=Ut.exec(s);)f[t[1].toLowerCase()]=t[2];t=f[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=x[n]=x[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(b<2)for(t in e)y[t]=[y[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),r(0,t),this}};if(g.promise(T).complete=m.add,T.success=T.done,T.error=T.fail,d.url=((t||d.url||en)+"").replace(zt,"").replace(Jt,tn[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=pe.trim(d.dataType||"*").toLowerCase().match(De)||[""],null==d.crossDomain&&(i=Gt.exec(d.url.toLowerCase()),d.crossDomain=!(!i||i[1]===tn[1]&&i[2]===tn[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(tn[3]||("http:"===tn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=pe.param(d.data,d.traditional)),U(Kt,d,n,T),2===b)return T;l=pe.event&&d.global,l&&0===pe.active++&&pe.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Yt.test(d.type),a=d.url,d.hasContent||(d.data&&(a=d.url+=(It.test(a)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Xt.test(a)?a.replace(Xt,"$1_="+Wt++):a+(It.test(a)?"&":"?")+"_="+Wt++)),d.ifModified&&(pe.lastModified[a]&&T.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&T.setRequestHeader("If-None-Match",pe.etag[a])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",d.contentType),T.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Zt+"; q=0.01":""):d.accepts["*"]);for(o in d.headers)T.setRequestHeader(o,d.headers[o]);if(d.beforeSend&&(d.beforeSend.call(p,T,d)===!1||2===b))return T.abort();w="abort";for(o in{success:1,error:1,complete:1})T[o](d[o]);if(c=U(Qt,d,n,T)){if(T.readyState=1,l&&h.trigger("ajaxSend",[T,d]),2===b)return T;d.async&&d.timeout>0&&(u=e.setTimeout(function(){T.abort("timeout")},d.timeout));try{b=1,c.send(v,r)}catch(C){if(!(b<2))throw C;r(-1,C)}}else r(-1,"No Transport");return T},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,r,i){return pe.isFunction(n)&&(i=i||r,r=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:i,data:n,success:r},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return fe.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:K(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var nn=/%20/g,rn=/\[\]$/,on=/\r?\n/g,an=/^(?:submit|button|image|reset|file)$/i,sn=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,r=[],i=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){i(this.name,this.value)});else for(n in e)Q(n,e[n],t,i);return r.join("&").replace(nn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&sn.test(this.nodeName)&&!an.test(e)&&(this.checked||!Be.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():re.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var un=0,ln={},cn=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),fe.cors=!!cn&&"withCredentials"in cn,cn=fe.ajax=!!cn,cn&&pe.ajaxTransport(function(t){if(!t.crossDomain||fe.cors){var n;return{send:function(r,i){var o,a=t.xhr(),s=++un;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)void 0!==r[o]&&a.setRequestHeader(o,r[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,r){var o,u,l;if(n&&(r||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,r)4!==a.readyState&&a.abort();else{l={},o=a.status,"string"==typeof a.responseText&&(l.text=a.responseText);try{u=a.statusText}catch(c){u=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=l.text?200:404}l&&i(o,u,l,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=re.head||pe("head")[0]||re.documentElement;return{send:function(r,i){t=re.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var fn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=fn.pop()||pe.expando+"_"+Wt++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(It.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||pe.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?pe(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,fn.push(i)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||re;var r=Te.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=y([e],t,i),i&&i.length&&pe(i).remove(),pe.merge([],r.childNodes))};var pn=pe.fn.load;return pe.fn.load=function(e,t,n){if("string"!=typeof e&&pn)return pn.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&pe.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?pe("
      ").append(pe.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=pe.css(e,"position"),f=pe(e),d={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=pe.css(e,"top"),u=pe.css(e,"left"),l=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,u])>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+i),"using"in t?t.using.call(e,d):f.css(d)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,i)?("undefined"!=typeof i.getBoundingClientRect&&(r=i.getBoundingClientRect()),n=te(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===pe.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(r,"marginTop",!0),left:t.left-n.left-pe.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){ +for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){layui.$=pe,e("jquery",pe)}),pe});!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
      '+(f?r.title[0]:r.title)+"
      ":"";return r.zIndex=s,t([r.shade?'
      ':"",'
      '+(e&&2!=r.type?"":u)+'
      '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
      '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
      '+e+"
      "}():"")+(r.resize?'':"")+"
      "],u,i('
      ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(t){s=t.find(".layui-layer-input"),s.val(e.value||"").focus(),"function"==typeof f&&f(t)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"";return a}(),content:'
        '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
      • '+(t[0].content||"no content")+"
      • ";i'+(t[i].content||"no content")+"";return a}()+"
      ",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
      '+(u.length>1?'':"")+'
      '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
      ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
      是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window);layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
    • "+(i.title||"unnaming")+"
    • ";return s[0]?s.before(r):n.append(r),o.append('
      '+(i.content||"")+"
      "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)});layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,n=layui.hint(),a=layui.device(),o={config:{},set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,r,e,i)}},l=function(){var e=this;return{upload:function(i){e.upload.call(e,i)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var t=this;t.config=i.extend({},t.config,o.config,e),t.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var t=this,e=t.config;e.elem=i(e.elem),e.bindAction=i(e.bindAction),t.file(),t.events()},p.prototype.file=function(){var e=this,t=e.config,n=e.elemFile=i(['"].join("")),o=t.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&t.elem.wrap('
      '),e.isFile()?(e.elemFile=t.elem,t.field=t.elem[0].name):t.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,t=e.config,n=i(''),a=i(['
      ',"
      "].join(""));i("#"+f)[0]||i("body").append(n),t.elem.next().hasClass(c)||(e.elemFile.wrap(a),t.elem.next("."+c).append(function(){var e=[];return layui.each(t.data,function(i,t){t="function"==typeof t?t():t,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return t.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var i=this;window.FileReader&&layui.each(i.chooseFiles,function(i,t){var n=new FileReader;n.readAsDataURL(t),n.onload=function(){e&&e(i,t,this.result)}})},p.prototype.upload=function(e,t){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var t=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&t+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:t,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,i){i="function"==typeof i?i():i,r.append(e,i)}),i.ajax({url:l.url,type:l.method,data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(i){t++,d(e,i),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=i("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var i,t=e.contents().find("body");try{i=t.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}i&&(clearInterval(p.timer),t.html(""),d(0,i))},30)},d=function(e,i){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof i)try{i=JSON.parse(i)}catch(t){return i={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(i,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var i=[];return layui.each(e||o.chooseFiles,function(e,t){i.push(t.name)}),i}(),g={preview:function(e){o.preview(e)},upload:function(e,i){var t={};t[e]=i,o.upload(t)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,i){o.files[e]=i}),o.files},resetFile:function(e,i,t){var n=new File([i],t);o.files=o.files||{},o.files[e]=n}},y=function(){if("choose"!==t&&!l.auto||(l.choose&&l.choose(g),"choose"!==t))return l.before&&l.before(g),a.ie?a.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,i){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(i))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var i=0,t=e||o.files||o.chooseFiles||r.files;return layui.each(t,function(){i++}),i}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,i){if(i.size>1024*l.size){var t=l.size/1024;t=t>=1?t.toFixed(2)+"MB":l.size+"KB",r.value="",F=t}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,t=e.config,o=function(i){e.chooseFiles={},layui.each(i,function(i,t){var n=(new Date).getTime();e.chooseFiles[n+"-"+i]=t})},l=function(i,n){var a=e.elemFile,o=i.length>1?i.length+"个文件":(i[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||t.choose||a.after(''+o+"")};t.elem.off("upload.start").on("upload.start",function(){var a=i(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=i.extend({},t,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||t.elem.off("upload.over").on("upload.over",function(){var e=i(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=i(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=i(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),t.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var i=this.files||[];o(i),t.auto?e.upload():l(i)}),t.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),t.elem.data("haveEvents")||(e.elemFile.on("change",function(){i(this).trigger("upload.change")}),t.elem.on("click",function(){e.isFile()||i(this).trigger("upload.start")}),t.drag&&t.elem.on("dragover",function(e){e.preventDefault(),i(this).trigger("upload.over")}).on("dragleave",function(e){i(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),i(this).trigger("upload.drop",e)}),t.bindAction.on("click",function(){i(this).trigger("upload.action")}),t.elem.data("haveEvents",!0))},o.render=function(e){var i=new p(e);return l.call(i)},e(r,o)});layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var t=this;return i.extend(!0,t.config,e),t},u.prototype.verify=function(e){var t=this;return i.extend(!0,t.config.verify,e),t},u.prototype.on=function(e,i){return layui.onevent.call(this,l,e,i)},u.prototype.val=function(e,t){var a=i(r+'[lay-filter="'+e+'"]');a.each(function(e,a){var n=i(this);layui.each(t,function(e,i){var t,a=n.find('[name="'+e+'"]');a[0]&&(t=a[0].type,"checkbox"===t?a[0].checked=i:"radio"===t?a.each(function(){this.value===i&&(this.checked=!0)}):a.val(i))})}),f.render(null,e)},u.prototype.render=function(e,t){var n=this,u=i(r+function(){return t?'[lay-filter="'+t+'"]':""}()),d={select:function(){var e,t="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=u.find("select"),v=function(t,l){i(t.target).parent().hasClass(n)&&!l||(i("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},y=function(t,u,f){var y,p=i(this),m=t.find("."+n),k=m.find("input"),g=t.find("dl"),x=g.children("dd"),b=this.selectedIndex;if(!u){var C=function(){var e=t.offset().top+t.outerHeight()+5-h.scrollTop(),i=g.outerHeight();b=p[0].selectedIndex,t.addClass(a+"ed"),x.removeClass(o),y=null,x.eq(b).addClass(s).siblings().removeClass(s),e+i>h.height()&&e>=i&&t.addClass(a+"up")},w=function(e){t.removeClass(a+"ed "+a+"up"),k.blur(),y=null,e||$(k.val(),function(e){e&&(d=g.find("."+s).html(),k&&k.val(d))})};m.on("click",function(e){t.hasClass(a+"ed")?w():(v(e,!0),C()),g.find("."+r).remove()}),m.find(".layui-edge").on("click",function(){k.focus()}),k.on("keyup",function(e){var i=e.keyCode;9===i&&C()}).on("keydown",function(e){var i=e.keyCode;9===i&&w();var t=function(i,a){var n,l;if(e.preventDefault(),a=function(){return a&&a[0]?a:y&&y[0]?y:x.eq(b)}(),l=a[i](),n=a[i]("dd"),l[0]){if(y=a[i](),!n[0]||n.hasClass(c))return t(i,y);n.addClass(s).siblings().removeClass(s);var r=g.children("dd.layui-this"),o=r.position().top,u=g.height(),d=r.height();o>u&&g.scrollTop(o+g.scrollTop()-u+d-5),o<0&&g.scrollTop(o+g.scrollTop())}};38===i&&t("prev"),40===i&&t("next"),13===i&&(e.preventDefault(),g.children("dd."+s).trigger("click"))});var $=function(e,t,a){var n=0;layui.each(x,function(){var t=i(this),l=t.text(),r=l.indexOf(e)===-1;(""===e||"blur"===a?e!==l:r)&&n++,"keyup"===a&&t[r?"addClass":"removeClass"](o)});var l=n===x.length;return t(l),l},T=function(e){var i=this.value,t=e.keyCode;return 9!==t&&13!==t&&37!==t&&38!==t&&39!==t&&40!==t&&($(i,function(e){e?g.find("."+r)[0]||g.append('

      无匹配项

      '):g.find("."+r).remove()},"keyup"),void(""===i&&g.find("."+r).remove()))};f&&k.on("keyup",T).on("blur",function(t){var a=p[0].selectedIndex;e=k,d=i(p[0].options[a]).html(),setTimeout(function(){$(k.val(),function(e){d||k.val("")},"blur")},200)}),x.on("click",function(){var e=i(this),a=e.attr("lay-value"),n=p.attr("lay-filter");return!e.hasClass(c)&&(e.hasClass("layui-select-tips")?k.val(""):(k.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),p.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:p[0],value:a,othis:t}),w(!0),!1)}),t.find("dl>dt").on("click",function(e){return!1}),i(document).off("click",v).on("click",v)}};f.each(function(e,l){var r=i(this),o=r.next("."+a),u=this.disabled,d=l.value,f=i(l.options[l.selectedIndex]),v=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var h="string"==typeof r.attr("lay-search"),p=v?v.value?t:v.innerHTML||t:t,m=i(['
      ','
      ','','
      ','
      ',function(e){var i=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?i.push("
      "+a.label+"
      "):i.push('
      '+a.innerHTML+"
      "):i.push('
      '+(a.innerHTML||t)+"
      ")}),0===i.length&&i.push('
      没有选项
      '),i.join("")}(r.find("*"))+"
      ","
      "].join(""));o[0]&&o.remove(),r.after(m),y.call(this,m,u,h)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},t=u.find("input[type=checkbox]"),a=function(e,t){var a=i(this);e.on("click",function(){var i=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(t[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(t[1]).find("em").text(n[0])),layui.event.call(a[0],l,t[2]+"("+i+")",{elem:a[0],value:a[0].value,othis:e}))})};t.each(function(t,n){var l=i(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=i(['
      ",function(){var e=n.title.replace(/\s/g,""),i={checkbox:[e?""+n.title+"":"",''].join(""),_switch:""+((n.checked?s[0]:s[1])||"")+""};return i[r]||i.checkbox}(),"
      "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",t=["",""],a=u.find("input[type=radio]"),n=function(a){var n=i(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=i(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(t[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(t[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=i(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var u=i(['
      ',''+t[l.checked?0:1]+"","
      "+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"
      ","
      "].join(""));r.after(u),n.call(this,u)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,i){i()}),n};var d=function(){var e=i(this),a=f.config.verify,s=null,o="layui-form-danger",c={},u=e.parents(r),d=u.find("*[lay-verify]"),v=e.parents("form")[0],h=u.find("input,select,textarea"),y=e.attr("lay-filter");if(layui.each(d,function(e,l){var r=i(this),c=r.attr("lay-verify").split("|"),u=r.attr("lay-verType"),d=r.val();if(r.removeClass(o),layui.each(c,function(e,i){var c,f="",v="function"==typeof a[i];if(a[i]){var c=v?f=a[i](d,l):!a[i][0].test(d);if(f=f||a[i][1],c)return"tips"===u?t.tips(f,function(){return"string"==typeof r.attr("lay-ignore")||"select"!==l.tagName.toLowerCase()&&!/^checkbox|radio$/.test(l.type)?r:r.next()}(),{tips:1}):"alert"===u?t.alert(f,{title:"提示",shadeClose:!0}):t.msg(f,{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}}),s)return s}),s)return!1;var p={};return layui.each(h,function(e,i){if(i.name=(i.name||"").replace(/^\s*|\s*&/,""),i.name){if(/^.*\[\]$/.test(i.name)){var t=i.name.match(/^(.*)\[\]$/g)[0];p[t]=0|p[t],i.name=i.name.replace(/^(.*)\[\]$/,"$1["+p[t]++ +"]")}/^checkbox|radio$/.test(i.type)&&!i.checked||(c[i.name]=i.value)}}),layui.event.call(this,l,"submit("+y+")",{elem:this,form:v,field:c})},f=new u,v=i(document),h=i(window);f.render(),v.on("reset",r,function(){var e=i(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),v.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)});layui.define("jquery",function(e){"use strict";var o=layui.$,a=layui.hint(),i="layui-tree-enter",r=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};r.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},r.prototype.tree=function(e,a){var i=this,r=i.options,n=a||r.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
        '),s=o(["
      • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return r.check?''+("checkbox"===r.check?t.checkbox[0]:"radio"===r.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
      • "].join(""));l&&(s.append(c),i.tree(c,n.children)),e.append(s),"function"==typeof r.click&&i.click(s,n),i.spread(s,n),r.drag&&i.drag(s,n)})},r.prototype.click=function(e,o){var a=this,i=a.options;e.children("a").on("click",function(e){layui.stope(e),i.click(o)})},r.prototype.spread=function(e,o){var a=this,i=(a.options,e.children(".layui-tree-spread")),r=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),r.removeClass("layui-show"),i.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),r.addClass("layui-show"),i.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};r[0]&&(i.on("click",l),n.on("dblclick",l))},r.prototype.on=function(e){var a=this,r=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),r.drag&&o(document).on("mousemove",function(e){var i=a.move;if(i.from){var r=(i.to,o('
        '));e.preventDefault(),o("."+t)[0]||o("body").append(r);var n=o("."+t)[0]?o("."+t):r;n.addClass("layui-show").html(i.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(i),e.to&&e.to.elem.children("a").removeClass(i),a.move={},o("."+t).remove())})},r.prototype.move={},r.prototype.drag=function(e,a){var r=this,t=(r.options,e.children("a")),n=function(){var t=o(this),n=r.move;n.from&&(n.to={item:a,elem:e},t.addClass(i))};t.on("mousedown",function(){var o=r.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=r.move;a.from&&(delete a.to,e.removeClass(i))})},e("tree",function(e){var i=new r(e=e||{}),t=o(e.elem);return t[0]?void i.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})});layui.define(["laytpl","laypage","layer","form"],function(e){"use strict";var t=layui.$,i=layui.laytpl,a=layui.laypage,l=layui.layer,n=layui.form,o=layui.hint(),r=layui.device(),d={config:{checkName:"LAY_CHECKED",indexName:"LAY_TABLE_INDEX"},cache:{},index:layui.table?layui.table.index+1e4:0,set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,s,e,t)}},c=function(){var e=this,t=e.config,i=t.id;return i&&(c.config[i]=t),{reload:function(t){e.reload.call(e,t)},config:t}},s="table",u=".layui-table",h="layui-hide",f="layui-none",y="layui-table-view",p=".layui-table-header",m=".layui-table-body",v=".layui-table-main",g=".layui-table-fixed",x=".layui-table-fixed-l",b=".layui-table-fixed-r",k=".layui-table-tool",C=".layui-table-page",w=".layui-table-sort",N="layui-table-edit",T="layui-table-hover",F=function(e){var t='{{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}}';return e=e||{},['',"","{{# layui.each(d.data.cols, function(i1, item1){ }}","","{{# layui.each(item1, function(i2, item2){ }}",'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}','{{# if(item2.fixed === "right"){ right = true; } }}',function(){return e.fixed&&"right"!==e.fixed?'{{# if(item2.fixed && item2.fixed !== "right"){ }}':"right"===e.fixed?'{{# if(item2.fixed === "right"){ }}':""}(),'",e.fixed?"{{# }; }}":"","{{# }); }}","","{{# }); }}","","
        ','
        1){ }}","group","{{# } else { }}","{{d.index}}-{{item2.field || i2}}",'{{# if(item2.type !== "normal"){ }}'," laytable-cell-{{ item2.type }}","{{# } }}","{{# } }}",'" {{#if(item2.align){}}align="{{item2.align}}"{{#}}}>','{{# if(item2.type === "checkbox"){ }}','',"{{# } else { }}",'{{item2.title||""}}',"{{# if(!(item2.colspan > 1) && item2.sort){ }}",'',"{{# } }}","{{# } }}","
        ","
        "].join("")},W=['',"","
        "].join(""),z=['
        ',"{{# if(d.data.toolbar){ }}",'
        ',"{{# } }}",'
        ',"{{# var left, right; }}",'
        ',F(),"
        ",'
        ',W,"
        ","{{# if(left){ }}",'
        ','
        ',F({fixed:!0}),"
        ",'
        ',W,"
        ","
        ","{{# }; }}","{{# if(right){ }}",'
        ','
        ',F({fixed:"right"}),'
        ',"
        ",'
        ',W,"
        ","
        ","{{# }; }}","
        ","{{# if(d.data.page){ }}",'
        ','
        ',"
        ","{{# } }}","","
        "].join(""),A=t(window),S=t(document),M=function(e){var i=this;i.index=++d.index,i.config=t.extend({},i.config,d.config,e),i.render()};M.prototype.config={limit:10,loading:!0,cellMinWidth:60,text:{none:"无数据"}},M.prototype.render=function(){var e=this,a=e.config;if(a.elem=t(a.elem),a.where=a.where||{},a.id=a.id||a.elem.attr("id"),a.request=t.extend({pageName:"page",limitName:"limit"},a.request),a.response=t.extend({statusName:"code",statusCode:0,msgName:"msg",dataName:"data",countName:"count"},a.response),"object"==typeof a.page&&(a.limit=a.page.limit||a.limit,a.limits=a.page.limits||a.limits,e.page=a.page.curr=a.page.curr||1,delete a.page.elem,delete a.page.jump),!a.elem[0])return e;e.setArea();var l=a.elem,n=l.next("."+y),o=e.elem=t(i(z).render({VIEW_CLASS:y,data:a,index:e.index}));if(a.index=e.index,n[0]&&n.remove(),l.after(o),e.layHeader=o.find(p),e.layMain=o.find(v),e.layBody=o.find(m),e.layFixed=o.find(g),e.layFixLeft=o.find(x),e.layFixRight=o.find(b),e.layTool=o.find(k),e.layPage=o.find(C),e.layTool.html(i(t(a.toolbar).html()||"").render(a)),a.height&&e.fullSize(),a.cols.length>1){var r=e.layFixed.find(p).find("th");r.height(e.layHeader.height()-1-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom")))}e.pullData(e.page),e.events()},M.prototype.initOpts=function(e){var t=this,i=(t.config,{checkbox:48,space:15,numbers:40});e.checkbox&&(e.type="checkbox"),e.space&&(e.type="space"),e.type||(e.type="normal"),"normal"!==e.type&&(e.unresize=!0,e.width=e.width||i[e.type])},M.prototype.setArea=function(){var e=this,t=e.config,i=0,a=0,l=0,n=0,o=t.width||function(){var e=function(i){var a,l;i=i||t.elem.parent(),a=i.width();try{l="none"===i.css("display")}catch(n){}return!i[0]||a&&!l?a:e(i.parent())};return e()}();e.eachCols(function(){i++}),o-=function(){return"line"===t.skin||"nob"===t.skin?2:i+1}(),layui.each(t.cols,function(t,i){layui.each(i,function(t,l){var r;return l?(e.initOpts(l),r=l.width||0,void(l.colspan>1||(/\d+%$/.test(r)?l.width=r=Math.floor(parseFloat(r)/100*o):r||(l.width=r=0,a++),n+=r))):void i.splice(t,1)})}),e.autoColNums=a,o>n&&a&&(l=(o-n)/a),layui.each(t.cols,function(e,i){layui.each(i,function(e,i){var a=i.minWidth||t.cellMinWidth;i.colspan>1||0===i.width&&(i.width=Math.floor(l>=a?l:a))})}),t.height&&/^full-\d+$/.test(t.height)&&(e.fullHeightGap=t.height.split("-")[1],t.height=A.height()-e.fullHeightGap)},M.prototype.reload=function(e){var i=this;i.config.data&&i.config.data.constructor===Array&&delete i.config.data,i.config=t.extend({},i.config,e),i.render()},M.prototype.page=1,M.prototype.pullData=function(e,i){var a=this,n=a.config,o=n.request,r=n.response,d=function(){"object"==typeof n.initSort&&a.sort(n.initSort.field,n.initSort.type)};if(a.startTime=(new Date).getTime(),n.url){var c={};c[o.pageName]=e,c[o.limitName]=n.limit;var s=t.extend(c,n.where);n.contentType&&0==n.contentType.indexOf("application/json")&&(s=JSON.stringify(s)),t.ajax({type:n.method||"get",url:n.url,contentType:n.contentType,data:s,dataType:"json",headers:n.headers||{},success:function(t){t[r.statusName]!=r.statusCode?(a.renderForm(),a.layMain.html('
        '+(t[r.msgName]||"返回的数据状态异常")+"
        ")):(a.renderData(t,e,t[r.countName]),d(),n.time=(new Date).getTime()-a.startTime+" ms"),i&&l.close(i),"function"==typeof n.done&&n.done(t,e,t[r.countName])},error:function(e,t){a.layMain.html('
        数据接口请求异常
        '),a.renderForm(),i&&l.close(i)}})}else if(n.data&&n.data.constructor===Array){var u={},h=e*n.limit-n.limit;u[r.dataName]=n.data.concat().splice(h,n.limit),u[r.countName]=n.data.length,a.renderData(u,e,n.data.length),d(),"function"==typeof n.done&&n.done(u,e,u[r.countName])}},M.prototype.eachCols=function(e){var i=t.extend(!0,[],this.config.cols),a=[],l=0;layui.each(i,function(e,t){layui.each(t,function(t,n){if(n.colspan>1){var o=0;l++,n.CHILD_COLS=[],layui.each(i[e+1],function(e,t){t.PARENT_COL||o==n.colspan||(t.PARENT_COL=l,n.CHILD_COLS.push(t),o+=t.colspan>1?t.colspan:1)})}n.PARENT_COL||a.push(n)})});var n=function(t){layui.each(t||a,function(t,i){return i.CHILD_COLS?n(i.CHILD_COLS):void e(t,i)})};n()},M.prototype.renderData=function(e,n,o,r){var c=this,s=c.config,u=e[s.response.dataName]||[],y=[],p=[],m=[],v=function(){return!r&&c.sortKey?c.sort(c.sortKey.field,c.sortKey.sort,!0):(layui.each(u,function(e,a){var l=[],o=[],u=[],h=e+s.limit*(n-1)+1;0!==a.length&&(r||(a[d.config.indexName]=e),c.eachCols(function(e,n){var r=n.field||e,f=a[r];c.getColElem(c.layHeader,r);if(void 0!==f&&null!==f||(f=""),!(n.colspan>1)){var y=['",'
        '+function(){var e=t.extend(!0,{LAY_INDEX:h},a);return"checkbox"===n.type?'":"numbers"===n.type?h:n.toolbar?i(t(n.toolbar).html()||"").render(e):n.templet?function(){return"function"==typeof n.templet?n.templet(e):i(t(n.templet).html()||String(f)).render(e)}():f}(),"
        "].join("");l.push(y),n.fixed&&"right"!==n.fixed&&o.push(y),"right"===n.fixed&&u.push(y)}}),y.push(''+l.join("")+""),p.push(''+o.join("")+""),m.push(''+u.join("")+""))}),c.layBody.scrollTop(0),c.layMain.find("."+f).remove(),c.layMain.find("tbody").html(y.join("")),c.layFixLeft.find("tbody").html(p.join("")),c.layFixRight.find("tbody").html(m.join("")),c.renderForm(),c.syncCheckAll(),c.haveInit?c.scrollPatch():setTimeout(function(){c.scrollPatch()},50),c.haveInit=!0,void l.close(c.tipsIndex))};return c.key=s.id||s.index,d.cache[c.key]=u,c.layPage[0===u.length&&1==n?"addClass":"removeClass"](h),r?v():0===u.length?(c.renderForm(),c.layFixed.remove(),c.layMain.find("tbody").html(""),c.layMain.find("."+f).remove(),c.layMain.append('
        '+s.text.none+"
        ")):(v(),void(s.page&&(s.page=t.extend({elem:"layui-table-page"+s.index,count:o,limit:s.limit,limits:s.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(c.page=e.curr,s.limit=e.limit,c.pullData(e.curr,c.loading()))}},s.page),s.page.count=o,a.render(s.page))))},M.prototype.getColElem=function(e,t){var i=this,a=i.config;return e.eq(0).find(".laytable-cell-"+(a.index+"-"+t)+":eq(0)")},M.prototype.renderForm=function(e){n.render(e,"LAY-table-"+this.index)},M.prototype.sort=function(e,i,a,l){var n,r,c=this,u={},h=c.config,f=h.elem.attr("lay-filter"),y=d.cache[c.key];"string"==typeof e&&c.layHeader.find("th").each(function(i,a){var l=t(this),o=l.data("field");if(o===e)return e=l,n=o,!1});try{var n=n||e.data("field");if(c.sortKey&&!a&&n===c.sortKey.field&&i===c.sortKey.sort)return;var p=c.layHeader.find("th .laytable-cell-"+h.index+"-"+n).find(w);c.layHeader.find("th").find(w).removeAttr("lay-sort"),p.attr("lay-sort",i||null),c.layFixed.find("th")}catch(m){return o.error("Table modules: Did not match to field")}c.sortKey={field:n,sort:i},"asc"===i?r=layui.sort(y,n):"desc"===i?r=layui.sort(y,n,!0):(r=layui.sort(y,d.config.indexName),delete c.sortKey),u[h.response.dataName]=r,c.renderData(u,c.page,c.count,!0),l&&layui.event.call(e,s,"sort("+f+")",{field:n,type:i})},M.prototype.loading=function(){var e=this,t=e.config;if(t.loading&&t.url)return l.msg("数据请求中",{icon:16,offset:[e.elem.offset().top+e.elem.height()/2-35-A.scrollTop()+"px",e.elem.offset().left+e.elem.width()/2-90-A.scrollLeft()+"px"],time:-1,anim:-1,fixed:!1})},M.prototype.setCheckData=function(e,t){var i=this,a=i.config,l=d.cache[i.key];l[e]&&l[e].constructor!==Array&&(l[e][a.checkName]=t)},M.prototype.syncCheckAll=function(){var e=this,t=e.config,i=e.layHeader.find('input[name="layTableCheckbox"]'),a=function(i){return e.eachCols(function(e,a){"checkbox"===a.type&&(a[t.checkName]=i)}),i};i[0]&&(d.checkStatus(e.key).isAll?(i[0].checked||(i.prop("checked",!0),e.renderForm("checkbox")),a(!0)):(i[0].checked&&(i.prop("checked",!1),e.renderForm("checkbox")),a(!1)))},M.prototype.getCssRule=function(e,t){var i=this,a=i.elem.find("style")[0],l=a.sheet||a.styleSheet||{},n=l.cssRules||l.rules;layui.each(n,function(a,l){if(l.selectorText===".laytable-cell-"+i.index+"-"+e)return t(l),!0})},M.prototype.fullSize=function(){var e,t=this,i=t.config,a=i.height;t.fullHeightGap&&(a=A.height()-t.fullHeightGap,a<135&&(a=135),t.elem.css("height",a)),e=parseFloat(a)-parseFloat(t.layHeader.height())-1,i.toolbar&&(e-=t.layTool.outerHeight()),i.page&&(e=e-t.layPage.outerHeight()-1),t.layMain.css("height",e)},M.prototype.getScrollWidth=function(e){var t=0;return e?t=e.offsetWidth-e.clientWidth:(e=document.createElement("div"),e.style.width="100px",e.style.height="100px",e.style.overflowY="scroll",document.body.appendChild(e),t=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),t},M.prototype.scrollPatch=function(){var e=this,i=e.layMain.children("table"),a=e.layMain.width()-e.layMain.prop("clientWidth"),l=e.layMain.height()-e.layMain.prop("clientHeight"),n=e.getScrollWidth(e.layMain[0]),o=i.outerWidth()-e.layMain.width();if(e.autoColNums&&o<5&&!e.scrollPatchWStatus){var r=e.layHeader.eq(0).find("thead th:last-child"),d=r.data("field");e.getCssRule(d,function(t){var i=t.style.width||r.outerWidth();t.style.width=parseFloat(i)-n-o+"px",e.layMain.height()-e.layMain.prop("clientHeight")>0&&(t.style.width=parseFloat(t.style.width)-1+"px"),e.scrollPatchWStatus=!0})}if(a&&l){if(!e.elem.find(".layui-table-patch")[0]){var c=t('
        ');c.find("div").css({width:a}),e.layHeader.eq(0).find("thead tr").append(c)}}else e.layHeader.eq(0).find(".layui-table-patch").remove();var s=e.layMain.height(),u=s-l;e.layFixed.find(m).css("height",i.height()>u?u:"auto"),e.layFixRight[o>0?"removeClass":"addClass"](h),e.layFixRight.css("right",a-1)},M.prototype.events=function(){var e,a=this,n=a.config,o=t("body"),c={},u=a.layHeader.find("th"),h=".layui-table-cell",f=n.elem.attr("lay-filter");u.on("mousemove",function(e){var i=t(this),a=i.offset().left,l=e.clientX-a;i.attr("colspan")>1||i.data("unresize")||c.resizeStart||(c.allowResize=i.width()-l<=10,o.css("cursor",c.allowResize?"col-resize":""))}).on("mouseleave",function(){t(this);c.resizeStart||o.css("cursor","")}).on("mousedown",function(e){var i=t(this);if(c.allowResize){var l=i.data("field");e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],a.getCssRule(l,function(e){var t=e.style.width||i.outerWidth();c.rule=e,c.ruleWidth=parseFloat(t),c.minWidth=i.data("minwidth")||n.cellMinWidth})}}),S.on("mousemove",function(t){if(c.resizeStart){if(t.preventDefault(),c.rule){var i=c.ruleWidth+t.clientX-c.offset[0];i');d[0].value=e.data("content")||o.text(),e.find("."+N)[0]||e.append(d),d.focus()}else o.find(".layui-form-switch,.layui-form-checkbox")[0]||Math.round(o.prop("scrollWidth"))>Math.round(o.outerWidth())&&(a.tipsIndex=l.tips(['
        ',o.html(),"
        ",''].join(""),o[0],{tips:[3,""],time:-1,anim:-1,maxWidth:r.ios||r.android?300:600,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){l.close(t)})}}))}),a.layBody.on("click","*[lay-event]",function(){var e=t(this),l=e.parents("tr").eq(0).data("index"),n=a.layBody.find('tr[data-index="'+l+'"]'),o="layui-table-click",r=d.cache[a.key][l];layui.event.call(this,s,"tool("+f+")",{data:d.clearCacheKey(r),event:e.attr("lay-event"),tr:n,del:function(){d.cache[a.key][l]=[],n.remove(),a.scrollPatch()},update:function(e){e=e||{},layui.each(e,function(e,l){if(e in r){var o,d=n.children('td[data-field="'+e+'"]');r[e]=l,a.eachCols(function(t,i){i.field==e&&i.templet&&(o=i.templet)}),d.children(h).html(o?i(t(o).html()||l).render(r):l),d.data("content",l)}})}}),n.addClass(o).siblings("tr").removeClass(o)}),a.layMain.on("scroll",function(){var e=t(this),i=e.scrollLeft(),n=e.scrollTop();a.layHeader.scrollLeft(i),a.layFixed.find(m).scrollTop(n),l.close(a.tipsIndex)}),A.on("resize",function(){a.fullSize(),a.scrollPatch()})},d.init=function(e,i){i=i||{};var a=this,l=t(e?'table[lay-filter="'+e+'"]':u+"[lay-data]"),n="Table element property lay-data configuration item has a syntax error: ";return l.each(function(){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){o.error(n+l)}var c=[],s=t.extend({elem:this,cols:[],data:[],skin:a.attr("lay-skin"),size:a.attr("lay-size"),even:"string"==typeof a.attr("lay-even")},d.config,i,l);e&&a.hide(),a.find("thead>tr").each(function(e){s.cols[e]=[],t(this).children().each(function(i){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){return o.error(n+l)}var d=t.extend({title:a.text(),colspan:a.attr("colspan")||0,rowspan:a.attr("rowspan")||0},l);d.colspan<2&&c.push(d),s.cols[e].push(d)})}),a.find("tbody>tr").each(function(e){var i=t(this),a={};i.children("td").each(function(e,i){var l=t(this),n=l.data("field");if(n)return a[n]=l.html()}),layui.each(c,function(e,t){var l=i.children("td").eq(e);a[t.field]=l.html()}),s.data[e]=a}),d.render(s)}),a},d.checkStatus=function(e){var t=0,i=0,a=[],l=d.cache[e]||[];return layui.each(l,function(e,l){return l.constructor===Array?void i++:void(l[d.config.checkName]&&(t++,a.push(d.clearCacheKey(l))))}),{data:a,isAll:!!l.length&&t===l.length-i}},c.config={},d.reload=function(e,i){var a=c.config[e];return i=i||{},a?(i.data&&i.data.constructor===Array&&delete a.data,d.render(t.extend(!0,{},a,i))):o.error("The ID option was not found in the table instance")},d.render=function(e){var t=new M(e);return c.call(t)},d.clearCacheKey=function(e){return e=t.extend({},e),delete e[d.config.checkName],delete e[d.config.indexName],e},d.init(),e(s,d)});layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
          ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
        "].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a",u=1;u<=i.length;u++){var r='
      • ";i.half&&parseInt(i.value)!==i.value&&u==Math.ceil(i.value)?n=n+'
      • ":n+=r}n+=""+(i.text?''+i.value+"星":"")+"";var c=i.elem,f=c.next("."+t);f[0]&&f.remove(),e.elemTemp=a(n),i.span=e.elemTemp.next("span"),i.setText&&i.setText(i.value),c.html(e.elemTemp),c.addClass("layui-inline"),i.readonly||e.action()},v.prototype.setvalue=function(e){var a=this,i=a.config;i.value=e,a.render()},v.prototype.action=function(){var e=this,i=e.config,l=e.elemTemp,n=l.find("i").width();l.children("li").each(function(e){var t=e+1,v=a(this);v.on("click",function(e){if(i.value=t,i.half){var o=e.pageX-a(this).offset().left;o<=n/2&&(i.value=i.value-.5)}i.text&&l.next("span").text(i.value+"星"),i.choose&&i.choose(i.value),i.setText&&i.setText(i.value)}),v.on("mousemove",function(e){if(l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+t+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half){var c=e.pageX-a(this).offset().left;c<=n/2&&v.children("i").addClass(u).removeClass(s)}}),v.on("mouseleave",function(){l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+Math.floor(i.value)+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half&&parseInt(i.value)!==i.value&&l.children("li:eq("+Math.floor(i.value)+")").children("i").addClass(u).removeClass(c)})})},v.prototype.events=function(){var e=this;e.config},i.render=function(e){var a=new v(e);return l.call(a)},e(n,i)});layui.define("jquery",function(e){"use strict";var t=layui.$,i={fixbar:function(e){var i,a,o="layui-fixbar",r="layui-fixbar-top",l=t(document),n=t("body");e=t.extend({showHeight:200},e),e.bar1=e.bar1===!0?"":e.bar1,e.bar2=e.bar2===!0?"":e.bar2,e.bgcolor=e.bgcolor?"background-color:"+e.bgcolor:"";var c=[e.bar1,e.bar2,""],g=t(['
          ',e.bar1?'
        • '+c[0]+"
        • ":"",e.bar2?'
        • '+c[1]+"
        • ":"",'
        • '+c[2]+"
        • ","
        "].join("")),u=g.find("."+r),s=function(){var t=l.scrollTop();t>=e.showHeight?i||(u.show(),i=1):i&&(u.hide(),i=0)};t("."+o)[0]||("object"==typeof e.css&&g.css(e.css),n.append(g),s(),g.find("li").on("click",function(){var i=t(this),a=i.attr("lay-type");"top"===a&&t("html,body").animate({scrollTop:0},200),e.click&&e.click.call(this,a)}),l.on("scroll",function(){clearTimeout(a),a=setTimeout(function(){s()},100)}))},countdown:function(e,t,i){var a=this,o="function"==typeof t,r=new Date(e).getTime(),l=new Date(!t||o?(new Date).getTime():t).getTime(),n=r-l,c=[Math.floor(n/864e5),Math.floor(n/36e5)%24,Math.floor(n/6e4)%60,Math.floor(n/1e3)%60];o&&(i=t);var g=setTimeout(function(){a.countdown(e,l+1e3,i)},1e3);return i&&i(n>0?c:[0,0,0,0],t,g),n<=0&&clearTimeout(g),g},timeAgo:function(e,t){var i=this,a=[[],[]],o=(new Date).getTime()-new Date(e).getTime();return o>6912e5?(o=new Date(e),a[0][0]=i.digit(o.getFullYear(),4),a[0][1]=i.digit(o.getMonth()+1),a[0][2]=i.digit(o.getDate()),t||(a[1][0]=i.digit(o.getHours()),a[1][1]=i.digit(o.getMinutes()),a[1][2]=i.digit(o.getSeconds())),a[0].join("-")+" "+a[1].join(":")):o>=864e5?(o/1e3/60/60/24|0)+"天前":o>=36e5?(o/1e3/60/60|0)+"小时前":o>=12e4?(o/1e3/60|0)+"分钟前":o<0?"未来":"刚刚"},digit:function(e,t){var i="";e=String(e),t=t||2;for(var a=e.length;a/g,">").replace(/'/g,"'").replace(/"/g,""")}};e("util",i)});layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)});layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
        1. '+o.replace(/[\r\t\n]+/g,"
        2. ")+"
        "),c.find(">.layui-code-h3")[0]||c.prepend('

        '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

        ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss");layui.define(["layer","form"],function(t){"use strict";var e=layui.$,i=layui.layer,a=layui.form,l=(layui.hint(),layui.device()),n="layedit",o="layui-show",r="layui-disabled",c=function(){var t=this;t.index=0,t.config={tool:["strong","italic","underline","del","|","left","center","right","|","link","unlink","face","image"],hideTool:[],height:280}};c.prototype.set=function(t){var i=this;return e.extend(!0,i.config,t),i},c.prototype.on=function(t,e){return layui.onevent(n,t,e)},c.prototype.build=function(t,i){i=i||{};var a=this,n=a.config,r="layui-layedit",c=e("string"==typeof t?"#"+t:t),u="LAY_layedit_"+ ++a.index,d=c.next("."+r),y=e.extend({},n,i),f=function(){var t=[],e={};return layui.each(y.hideTool,function(t,i){e[i]=!0}),layui.each(y.tool,function(i,a){C[a]&&!e[a]&&t.push(C[a])}),t.join("")}(),m=e(['
        ','
        '+f+"
        ",'
        ','',"
        ","
        "].join(""));return l.ie&&l.ie<8?c.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),s.call(a,m,c[0],y),c.addClass("layui-hide").after(m),a.index)},c.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},c.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},c.prototype.setContent=function(t,i,a){var l=u(t);l[0]&&(a?e(l[0].document.body).append(i):e(l[0].document.body).html(i),layedit.sync(t))},c.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},c.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var s=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),c=o.find("head"),s=e([""].join("")),u=o.find("body");c.append(s),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,c=e(r.body);c.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

        ")}}),e(n).parents("form").on("submit",function(){var t=c.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),c.on("paste",function(e){r.execCommand("formatBlock",!1,"

        "),setTimeout(function(){f.call(t,c),n.value=c.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),c={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o.render({url:r.url,method:r.type,elem:e(n).find("input")[0],done:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},s=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

        "),setTimeout(function(){o.focus()},10)):c[a]&&c[a].call(this,u),h.call(t,s,i)}},d=/image/;s.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,s),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

          ','
        • ','','
          ','',"
          ","
        • ",'
        • ','','
          ','",'","
          ","
        • ",'
        • ','','',"
        • ","
        "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
      • '+e+'
      • ')}),'
          '+t.join("")+"
        "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
          ','
        • ','','
          ','","
          ","
        • ",'
        • ','','
          ','',"
          ","
        • ",'
        • ','','',"
        • ","
        "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new c;t(n,w)}); \ No newline at end of file diff --git a/agent-analyer/src/main/resources/static/js/layui/layui.js b/agent-analyer/src/main/resources/static/js/layui/layui.js new file mode 100644 index 0000000..c29e05b --- /dev/null +++ b/agent-analyer/src/main/resources/static/js/layui/layui.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;!function(e){"use strict";var t=document,n={modules:{},status:{},timeout:10,event:{}},o=function(){this.v="2.3.0"},r=function(){var e=t.currentScript?t.currentScript.src:function(){for(var e,n=t.scripts,o=n.length-1,r=o;r>0;r--)if("interactive"===n[r].readyState){e=n[r].src;break}return e||n[o].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),a=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},i="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",rate:"modules/rate",carousel:"modules/carousel",flow:"modules/flow",util:"modules/util",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};o.prototype.cache=n,o.prototype.define=function(e,t){var o=this,r="function"==typeof e,a=function(){var e=function(e,t){layui[e]=t,n.status[e]=!0};return"function"==typeof t&&t(function(o,r){e(o,r),n.callback[o]=function(){t(e)}}),this};return r&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?a.call(o):(o.use(e,a),o)},o.prototype.use=function(e,o,l){function s(e,t){var o="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||o.test((e.currentTarget||e.srcElement).readyState))&&(n.modules[d]=t,f.removeChild(v),function r(){return++m>1e3*n.timeout/4?a(d+" is not a valid module"):void(n.status[d]?c():setTimeout(r,4))}())}function c(){l.push(layui[d]),e.length>1?y.use(e.slice(1),o,l):"function"==typeof o&&o.apply(layui,l)}var y=this,p=n.dir=n.dir?n.dir:r,f=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,n){"jquery"===n&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var d=e[0],m=0;if(l=l||[],n.host=n.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[d]||!layui["layui.all"]&&layui["layui.mobile"]&&u[d])return c(),y;if(n.modules[d])!function g(){return++m>1e3*n.timeout/4?a(d+" is not a valid module"):void("string"==typeof n.modules[d]&&n.status[d]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[d]?p+"lay/":/^\{\/\}/.test(y.modules[d])?"":n.base||"")+(y.modules[d]||d)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=n.version===!0?n.v||(new Date).getTime():n.version||"";return e?"?v="+e:""}(),f.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||i?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),n.modules[d]=h}return y},o.prototype.getStyle=function(t,n){var o=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return o[o.getPropertyValue?"getPropertyValue":"getAttribute"](n)},o.prototype.link=function(e,o,r){var i=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof o&&(r=o);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(n.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof o?i:(function p(){return++y>1e3*n.timeout/100?a(e+" timeout"):void(1989===parseInt(i.getStyle(t.getElementById(c),"width"))?function(){o()}():setTimeout(p,100))}(),i)},n.callback={},o.prototype.factory=function(e){if(layui[e])return"function"==typeof n.callback[e]?n.callback[e]:null},o.prototype.addcss=function(e,t,o){return layui.link(n.dir+"css/"+e,t,o)},o.prototype.img=function(e,t,n){var o=new Image;return o.src=e,o.complete?t(o):(o.onload=function(){o.onload=null,"function"==typeof t&&t(o)},void(o.onerror=function(e){o.onerror=null,"function"==typeof n&&n(e)}))},o.prototype.config=function(e){e=e||{};for(var t in e)n[t]=e[t];return this},o.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),o.prototype.extend=function(e){var t=this;e=e||{};for(var n in e)t[n]||t.modules[n]?a("模块名 "+n+" 已被占用"):t.modules[n]=e[n];return t},o.prototype.router=function(e){var t=this,e=e||location.hash,n={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),n.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),n.search[t[0]]=t[1]}():n.path.push(t)}),n):n},o.prototype.data=function(t,n,o){if(t=t||"layui",o=o||localStorage,e.JSON&&e.JSON.parse){if(null===n)return delete o[t];n="object"==typeof n?n:{key:n};try{var r=JSON.parse(o[t])}catch(a){var r={}}return"value"in n&&(r[n.key]=n.value),n.remove&&delete r[n.key],o[t]=JSON.stringify(r),n.key?r[n.key]:r}},o.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},o.prototype.device=function(t){var n=navigator.userAgent.toLowerCase(),o=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(n.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(n)?"windows":/linux/.test(n)?"linux":/iphone|ipod|ipad|ios/.test(n)?"ios":/mac/.test(n)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((n.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:o("micromessenger")};return t&&!r[t]&&(r[t]=o(t)),r.android=/android/.test(n),r.ios="ios"===r.os,r},o.prototype.hint=function(){return{error:a}},o.prototype.each=function(e,t){var n,o=this;if("function"!=typeof t)return o;if(e=e||[],e.constructor===Object){for(n in e)if(t.call(e[n],n,e[n]))break}else for(n=0;na?1:r"].join("")),o=t.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&t.elem.wrap('
        '),e.isFile()?(e.elemFile=t.elem,t.field=t.elem[0].name):t.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,t=e.config,n=i(''),a=i(['
        ',"
        "].join(""));i("#"+f)[0]||i("body").append(n),t.elem.next().hasClass(c)||(e.elemFile.wrap(a),t.elem.next("."+c).append(function(){var e=[];return layui.each(t.data,function(i,t){t="function"==typeof t?t():t,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return t.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var i=this;window.FileReader&&layui.each(i.chooseFiles,function(i,t){var n=new FileReader;n.readAsDataURL(t),n.onload=function(){e&&e(i,t,this.result)}})},p.prototype.upload=function(e,t){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var t=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&t+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:t,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,i){i="function"==typeof i?i():i,r.append(e,i)}),i.ajax({url:l.url,type:l.method,data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(i){t++,d(e,i),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=i("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var i,t=e.contents().find("body");try{i=t.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}i&&(clearInterval(p.timer),t.html(""),d(0,i))},30)},d=function(e,i){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof i)try{i=JSON.parse(i)}catch(t){return i={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(i,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var i=[];return layui.each(e||o.chooseFiles,function(e,t){i.push(t.name)}),i}(),g={preview:function(e){o.preview(e)},upload:function(e,i){var t={};t[e]=i,o.upload(t)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,i){o.files[e]=i}),o.files},resetFile:function(e,i,t){var n=new File([i],t);o.files=o.files||{},o.files[e]=n}},y=function(){if("choose"!==t&&!l.auto||(l.choose&&l.choose(g),"choose"!==t))return l.before&&l.before(g),a.ie?a.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,i){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(i))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var i=0,t=e||o.files||o.chooseFiles||r.files;return layui.each(t,function(){i++}),i}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,i){if(i.size>1024*l.size){var t=l.size/1024;t=t>=1?t.toFixed(2)+"MB":l.size+"KB",r.value="",F=t}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,t=e.config,o=function(i){e.chooseFiles={},layui.each(i,function(i,t){var n=(new Date).getTime();e.chooseFiles[n+"-"+i]=t})},l=function(i,n){var a=e.elemFile,o=i.length>1?i.length+"个文件":(i[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||t.choose||a.after(''+o+"")};t.elem.off("upload.start").on("upload.start",function(){var a=i(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=i.extend({},t,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||t.elem.off("upload.over").on("upload.over",function(){var e=i(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=i(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=i(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),t.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var i=this.files||[];o(i),t.auto?e.upload():l(i)}),t.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),t.elem.data("haveEvents")||(e.elemFile.on("change",function(){i(this).trigger("upload.change")}),t.elem.on("click",function(){e.isFile()||i(this).trigger("upload.start")}),t.drag&&t.elem.on("dragover",function(e){e.preventDefault(),i(this).trigger("upload.over")}).on("dragleave",function(e){i(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),i(this).trigger("upload.drop",e)}),t.bindAction.on("click",function(){i(this).trigger("upload.action")}),t.elem.data("haveEvents",!0))},o.render=function(e){var i=new p(e);return l.call(i)},e(r,o)}); \ No newline at end of file diff --git a/pom.xml b/agent/pom.xml similarity index 100% rename from pom.xml rename to agent/pom.xml diff --git a/src/main/java/com/wenshuo/agent/Agent.java b/agent/src/main/java/com/wenshuo/agent/Agent.java similarity index 100% rename from src/main/java/com/wenshuo/agent/Agent.java rename to agent/src/main/java/com/wenshuo/agent/Agent.java diff --git a/src/main/java/com/wenshuo/agent/AgentUtils.java b/agent/src/main/java/com/wenshuo/agent/AgentUtils.java similarity index 100% rename from src/main/java/com/wenshuo/agent/AgentUtils.java rename to agent/src/main/java/com/wenshuo/agent/AgentUtils.java diff --git a/src/main/java/com/wenshuo/agent/ConfigConsts.java b/agent/src/main/java/com/wenshuo/agent/ConfigConsts.java similarity index 100% rename from src/main/java/com/wenshuo/agent/ConfigConsts.java rename to agent/src/main/java/com/wenshuo/agent/ConfigConsts.java diff --git a/src/main/java/com/wenshuo/agent/ConfigUtils.java b/agent/src/main/java/com/wenshuo/agent/ConfigUtils.java similarity index 100% rename from src/main/java/com/wenshuo/agent/ConfigUtils.java rename to agent/src/main/java/com/wenshuo/agent/ConfigUtils.java diff --git a/src/main/java/com/wenshuo/agent/NamedThreadFactory.java b/agent/src/main/java/com/wenshuo/agent/NamedThreadFactory.java similarity index 100% rename from src/main/java/com/wenshuo/agent/NamedThreadFactory.java rename to agent/src/main/java/com/wenshuo/agent/NamedThreadFactory.java diff --git a/src/main/java/com/wenshuo/agent/PojoDetector.java b/agent/src/main/java/com/wenshuo/agent/PojoDetector.java similarity index 100% rename from src/main/java/com/wenshuo/agent/PojoDetector.java rename to agent/src/main/java/com/wenshuo/agent/PojoDetector.java diff --git a/src/main/java/com/wenshuo/agent/applog/AppLogFactory.java b/agent/src/main/java/com/wenshuo/agent/applog/AppLogFactory.java similarity index 100% rename from src/main/java/com/wenshuo/agent/applog/AppLogFactory.java rename to agent/src/main/java/com/wenshuo/agent/applog/AppLogFactory.java diff --git a/src/main/java/com/wenshuo/agent/applog/CommonsLoggingAppLog.java b/agent/src/main/java/com/wenshuo/agent/applog/CommonsLoggingAppLog.java similarity index 100% rename from src/main/java/com/wenshuo/agent/applog/CommonsLoggingAppLog.java rename to agent/src/main/java/com/wenshuo/agent/applog/CommonsLoggingAppLog.java diff --git a/src/main/java/com/wenshuo/agent/applog/ConsoleAppLog.java b/agent/src/main/java/com/wenshuo/agent/applog/ConsoleAppLog.java similarity index 100% rename from src/main/java/com/wenshuo/agent/applog/ConsoleAppLog.java rename to agent/src/main/java/com/wenshuo/agent/applog/ConsoleAppLog.java diff --git a/src/main/java/com/wenshuo/agent/applog/IAppLog.java b/agent/src/main/java/com/wenshuo/agent/applog/IAppLog.java similarity index 100% rename from src/main/java/com/wenshuo/agent/applog/IAppLog.java rename to agent/src/main/java/com/wenshuo/agent/applog/IAppLog.java diff --git a/src/main/java/com/wenshuo/agent/applog/Slf4jAppLog.java b/agent/src/main/java/com/wenshuo/agent/applog/Slf4jAppLog.java similarity index 100% rename from src/main/java/com/wenshuo/agent/applog/Slf4jAppLog.java rename to agent/src/main/java/com/wenshuo/agent/applog/Slf4jAppLog.java diff --git a/src/main/java/com/wenshuo/agent/javassist/ByteArrayClassPath.java b/agent/src/main/java/com/wenshuo/agent/javassist/ByteArrayClassPath.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/ByteArrayClassPath.java rename to agent/src/main/java/com/wenshuo/agent/javassist/ByteArrayClassPath.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CannotCompileException.java b/agent/src/main/java/com/wenshuo/agent/javassist/CannotCompileException.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CannotCompileException.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CannotCompileException.java diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassClassPath.java b/agent/src/main/java/com/wenshuo/agent/javassist/ClassClassPath.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/ClassClassPath.java rename to agent/src/main/java/com/wenshuo/agent/javassist/ClassClassPath.java diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassMap.java b/agent/src/main/java/com/wenshuo/agent/javassist/ClassMap.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/ClassMap.java rename to agent/src/main/java/com/wenshuo/agent/javassist/ClassMap.java diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassPath.java b/agent/src/main/java/com/wenshuo/agent/javassist/ClassPath.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/ClassPath.java rename to agent/src/main/java/com/wenshuo/agent/javassist/ClassPath.java diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassPool.java b/agent/src/main/java/com/wenshuo/agent/javassist/ClassPool.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/ClassPool.java rename to agent/src/main/java/com/wenshuo/agent/javassist/ClassPool.java diff --git a/src/main/java/com/wenshuo/agent/javassist/ClassPoolTail.java b/agent/src/main/java/com/wenshuo/agent/javassist/ClassPoolTail.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/ClassPoolTail.java rename to agent/src/main/java/com/wenshuo/agent/javassist/ClassPoolTail.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CodeConverter.java b/agent/src/main/java/com/wenshuo/agent/javassist/CodeConverter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CodeConverter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CodeConverter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtArray.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtArray.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtArray.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtArray.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtBehavior.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtBehavior.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtBehavior.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtBehavior.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtClass.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtClass.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtClass.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtClass.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtClassType.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtClassType.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtClassType.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtClassType.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtConstructor.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtConstructor.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtConstructor.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtConstructor.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtField.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtField.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtField.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtField.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtMember.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtMember.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtMember.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtMember.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtMethod.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtMethod.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtMethod.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtMethod.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewClass.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtNewClass.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtNewClass.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtNewClass.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewConstructor.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtNewConstructor.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtNewConstructor.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtNewConstructor.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewMethod.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtNewMethod.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtNewMethod.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtNewMethod.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewNestedClass.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtNewNestedClass.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtNewNestedClass.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtNewNestedClass.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedConstructor.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedMethod.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedMethod.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtNewWrappedMethod.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtNewWrappedMethod.java diff --git a/src/main/java/com/wenshuo/agent/javassist/CtPrimitiveType.java b/agent/src/main/java/com/wenshuo/agent/javassist/CtPrimitiveType.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/CtPrimitiveType.java rename to agent/src/main/java/com/wenshuo/agent/javassist/CtPrimitiveType.java diff --git a/src/main/java/com/wenshuo/agent/javassist/Loader.java b/agent/src/main/java/com/wenshuo/agent/javassist/Loader.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/Loader.java rename to agent/src/main/java/com/wenshuo/agent/javassist/Loader.java diff --git a/src/main/java/com/wenshuo/agent/javassist/LoaderClassPath.java b/agent/src/main/java/com/wenshuo/agent/javassist/LoaderClassPath.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/LoaderClassPath.java rename to agent/src/main/java/com/wenshuo/agent/javassist/LoaderClassPath.java diff --git a/src/main/java/com/wenshuo/agent/javassist/Modifier.java b/agent/src/main/java/com/wenshuo/agent/javassist/Modifier.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/Modifier.java rename to agent/src/main/java/com/wenshuo/agent/javassist/Modifier.java diff --git a/src/main/java/com/wenshuo/agent/javassist/NotFoundException.java b/agent/src/main/java/com/wenshuo/agent/javassist/NotFoundException.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/NotFoundException.java rename to agent/src/main/java/com/wenshuo/agent/javassist/NotFoundException.java diff --git a/src/main/java/com/wenshuo/agent/javassist/SerialVersionUID.java b/agent/src/main/java/com/wenshuo/agent/javassist/SerialVersionUID.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/SerialVersionUID.java rename to agent/src/main/java/com/wenshuo/agent/javassist/SerialVersionUID.java diff --git a/src/main/java/com/wenshuo/agent/javassist/Translator.java b/agent/src/main/java/com/wenshuo/agent/javassist/Translator.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/Translator.java rename to agent/src/main/java/com/wenshuo/agent/javassist/Translator.java diff --git a/src/main/java/com/wenshuo/agent/javassist/URLClassPath.java b/agent/src/main/java/com/wenshuo/agent/javassist/URLClassPath.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/URLClassPath.java rename to agent/src/main/java/com/wenshuo/agent/javassist/URLClassPath.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/AccessFlag.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/AccessFlag.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/AccessFlag.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/AccessFlag.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationDefaultAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/AnnotationsAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/AttributeInfo.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/BadBytecode.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/BadBytecode.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/BadBytecode.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/BadBytecode.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/BootstrapMethodsAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteArray.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteArray.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ByteArray.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteArray.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteStream.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteStream.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ByteStream.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ByteStream.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/Bytecode.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/Bytecode.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/Bytecode.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/Bytecode.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFile.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFile.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFile.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFile.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFilePrinter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ClassFileWriter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAnalyzer.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeIterator.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeIterator.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/CodeIterator.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/CodeIterator.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstPool.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstPool.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ConstPool.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstPool.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ConstantAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/DeprecatedAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/Descriptor.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/Descriptor.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/Descriptor.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/Descriptor.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/DuplicateMemberException.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/EnclosingMethodAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionTable.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ExceptionsAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/FieldInfo.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/FieldInfo.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/FieldInfo.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/FieldInfo.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/InnerClassesAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/InstructionPrinter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/LineNumberAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/LocalVariableTypeAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/LongVector.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/LongVector.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/LongVector.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/LongVector.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodInfo.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodInfo.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/MethodInfo.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodInfo.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/MethodParametersAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/Mnemonic.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/Mnemonic.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/Mnemonic.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/Mnemonic.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/Opcode.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/Opcode.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/Opcode.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/Opcode.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/ParameterAnnotationsAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/SignatureAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/SourceFileAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMap.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMap.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/StackMap.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMap.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMapTable.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMapTable.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/StackMapTable.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/StackMapTable.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/SyntheticAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/TypeAnnotationsAttribute.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Analyzer.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/ControlFlow.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Executor.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Frame.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/FramePrinter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/IntQueue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiArrayType.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/MultiType.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Subroutine.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/SubroutineScanner.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Type.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Type.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Type.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Type.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Util.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Util.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Util.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/Util.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/analysis/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/Annotation.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationImpl.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/AnnotationsWriter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ArrayMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/BooleanMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ByteMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/CharMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ClassMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/DoubleMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/EnumMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/FloatMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/IntegerMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/LongMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/MemberValueVisitor.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/NoSuchClassError.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/ShortMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/StringMemberValue.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/TypeAnnotationsWriter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/annotation/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/BasicBlock.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/MapMaker.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/Tracer.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeData.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypeTag.java diff --git a/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java b/agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java rename to agent/src/main/java/com/wenshuo/agent/javassist/bytecode/stackmap/TypedBlock.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/AccessorMaker.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/AccessorMaker.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/AccessorMaker.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/AccessorMaker.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/CodeGen.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/CodeGen.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/CodeGen.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/CodeGen.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/CompileError.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/CompileError.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/CompileError.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/CompileError.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/Javac.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/Javac.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/Javac.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/Javac.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/JvstCodeGen.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/JvstTypeChecker.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/KeywordTable.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/KeywordTable.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/KeywordTable.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/KeywordTable.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/Lex.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/Lex.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/Lex.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/Lex.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/MemberCodeGen.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/MemberResolver.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/MemberResolver.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/MemberResolver.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/MemberResolver.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/NoFieldException.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/NoFieldException.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/NoFieldException.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/NoFieldException.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/Parser.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/Parser.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/Parser.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/Parser.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ProceedHandler.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ProceedHandler.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ProceedHandler.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ProceedHandler.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/SymbolTable.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/SymbolTable.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/SymbolTable.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/SymbolTable.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/SyntaxError.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/SyntaxError.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/SyntaxError.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/SyntaxError.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/TokenId.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/TokenId.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/TokenId.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/TokenId.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/TypeChecker.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/TypeChecker.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/TypeChecker.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/TypeChecker.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTList.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTList.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTList.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTList.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTree.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTree.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTree.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ASTree.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/ArrayInit.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/AssignExpr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/BinExpr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CallExpr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CastExpr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/CondExpr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Declarator.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Declarator.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/Declarator.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Declarator.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/DoubleConst.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Expr.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Expr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/Expr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Expr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/FieldDecl.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/InstanceOfExpr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/IntConst.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/IntConst.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/IntConst.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/IntConst.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Keyword.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Keyword.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/Keyword.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Keyword.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Member.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Member.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/Member.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Member.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/MethodDecl.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/NewExpr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Pair.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Pair.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/Pair.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Pair.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Stmnt.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/StringL.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/StringL.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/StringL.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/StringL.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Symbol.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Symbol.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/Symbol.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Symbol.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Variable.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Variable.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/Variable.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Variable.java diff --git a/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Visitor.java b/agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Visitor.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/compiler/ast/Visitor.java rename to agent/src/main/java/com/wenshuo/agent/javassist/compiler/ast/Visitor.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformAccessArrayField.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformAfter.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformAfter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/TransformAfter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformAfter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformBefore.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformBefore.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/TransformBefore.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformBefore.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformCall.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformCall.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/TransformCall.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformCall.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformFieldAccess.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformNew.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformNew.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/TransformNew.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformNew.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformNewClass.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformNewClass.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/TransformNewClass.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformNewClass.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformReadField.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformReadField.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/TransformReadField.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformReadField.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/TransformWriteField.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformWriteField.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/TransformWriteField.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/TransformWriteField.java diff --git a/src/main/java/com/wenshuo/agent/javassist/convert/Transformer.java b/agent/src/main/java/com/wenshuo/agent/javassist/convert/Transformer.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/convert/Transformer.java rename to agent/src/main/java/com/wenshuo/agent/javassist/convert/Transformer.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/Cast.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/Cast.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/Cast.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/Cast.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/ConstructorCall.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/ConstructorCall.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/ConstructorCall.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/ConstructorCall.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/Expr.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/Expr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/Expr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/Expr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/ExprEditor.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/ExprEditor.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/ExprEditor.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/ExprEditor.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/FieldAccess.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/FieldAccess.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/FieldAccess.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/FieldAccess.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/Handler.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/Handler.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/Handler.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/Handler.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/Instanceof.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/Instanceof.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/Instanceof.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/Instanceof.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/MethodCall.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/MethodCall.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/MethodCall.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/MethodCall.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/NewArray.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/NewArray.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/NewArray.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/NewArray.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/NewExpr.java b/agent/src/main/java/com/wenshuo/agent/javassist/expr/NewExpr.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/NewExpr.java rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/NewExpr.java diff --git a/src/main/java/com/wenshuo/agent/javassist/expr/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/expr/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/expr/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/expr/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/Cflow.java b/agent/src/main/java/com/wenshuo/agent/javassist/runtime/Cflow.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/runtime/Cflow.java rename to agent/src/main/java/com/wenshuo/agent/javassist/runtime/Cflow.java diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/Desc.java b/agent/src/main/java/com/wenshuo/agent/javassist/runtime/Desc.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/runtime/Desc.java rename to agent/src/main/java/com/wenshuo/agent/javassist/runtime/Desc.java diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/DotClass.java b/agent/src/main/java/com/wenshuo/agent/javassist/runtime/DotClass.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/runtime/DotClass.java rename to agent/src/main/java/com/wenshuo/agent/javassist/runtime/DotClass.java diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/Inner.java b/agent/src/main/java/com/wenshuo/agent/javassist/runtime/Inner.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/runtime/Inner.java rename to agent/src/main/java/com/wenshuo/agent/javassist/runtime/Inner.java diff --git a/src/main/java/com/wenshuo/agent/javassist/runtime/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/runtime/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/runtime/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/runtime/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java b/agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java rename to agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPool.java diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java b/agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java rename to agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactory.java diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java b/agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java rename to agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolFactoryImpl.java diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java b/agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java rename to agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepository.java diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java b/agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java rename to agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java b/agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java rename to agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/SoftValueHashMap.java diff --git a/src/main/java/com/wenshuo/agent/javassist/scopedpool/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/scopedpool/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/scopedpool/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/Callback.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/Callback.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/Callback.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/Callback.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/Dump.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/Dump.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/Dump.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/Dump.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/framedump.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/framedump.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/framedump.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/framedump.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/tools/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotCreateException.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotInvokeException.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/CannotReflectException.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/ClassMetaobject.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Compiler.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Compiler.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/Compiler.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Compiler.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Loader.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Loader.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/Loader.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Loader.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metalevel.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Metaobject.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Reflection.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Reflection.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/Reflection.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Reflection.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Sample.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Sample.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/Sample.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/Sample.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/reflect/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/reflect/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/reflect/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/AppletServer.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectImporter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/ObjectNotFoundException.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Proxy.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Proxy.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/rmi/Proxy.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Proxy.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteException.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/RemoteRef.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Sample.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Sample.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/rmi/Sample.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/Sample.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/StubGenerator.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/rmi/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/rmi/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/rmi/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/web/BadHttpRequest.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/web/Viewer.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/web/Viewer.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/web/Viewer.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/web/Viewer.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/web/Webserver.java b/agent/src/main/java/com/wenshuo/agent/javassist/tools/web/Webserver.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/web/Webserver.java rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/web/Webserver.java diff --git a/src/main/java/com/wenshuo/agent/javassist/tools/web/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/tools/web/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/tools/web/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/tools/web/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/util/HotSwapper.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/HotSwapper.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/HotSwapper.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/HotSwapper.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/util/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/util/package.html diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/FactoryHelper.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodFilter.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/MethodHandler.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/Proxy.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/Proxy.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/Proxy.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/Proxy.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyFactory.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObject.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectInputStream.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/ProxyObjectOutputStream.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/RuntimeSupport.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/SecurityActions.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/SerializedProxy.java diff --git a/src/main/java/com/wenshuo/agent/javassist/util/proxy/package.html b/agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/package.html similarity index 100% rename from src/main/java/com/wenshuo/agent/javassist/util/proxy/package.html rename to agent/src/main/java/com/wenshuo/agent/javassist/util/proxy/package.html diff --git a/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java b/agent/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java similarity index 100% rename from src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java rename to agent/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java diff --git a/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java b/agent/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java similarity index 100% rename from src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java rename to agent/src/main/java/com/wenshuo/agent/log/MethodExecuteJSONformatter.java diff --git a/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java b/agent/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java similarity index 100% rename from src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java rename to agent/src/main/java/com/wenshuo/agent/log/OutputLogRunnable.java diff --git a/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java b/agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java similarity index 100% rename from src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java rename to agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java diff --git a/src/main/resources/agent.properties b/agent/src/main/resources/agent.properties similarity index 100% rename from src/main/resources/agent.properties rename to agent/src/main/resources/agent.properties diff --git a/src/test/java/com/wenshuo/agent/test/TestCtMethod.java b/agent/src/test/java/com/wenshuo/agent/test/TestCtMethod.java similarity index 100% rename from src/test/java/com/wenshuo/agent/test/TestCtMethod.java rename to agent/src/test/java/com/wenshuo/agent/test/TestCtMethod.java From b6b19c41dc253db3134e7089764c59dff8501817 Mon Sep 17 00:00:00 2001 From: fanzhongwei Date: Tue, 10 Sep 2024 10:29:58 +0800 Subject: [PATCH 22/25] =?UTF-8?q?=E6=B7=BB=E5=8A=A0agent-analyzer=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- agent-analyer/agent-analyzer.iml | 99 ----------- agent-analyer/bin/README.md | 28 ++++ agent-analyer/bin/run.sh | 36 ++++ agent-analyer/bin/stop.sh | 18 ++ agent-analyer/pom.xml | 276 +++++++++++++++---------------- 6 files changed, 221 insertions(+), 238 deletions(-) delete mode 100644 agent-analyer/agent-analyzer.iml create mode 100644 agent-analyer/bin/README.md create mode 100755 agent-analyer/bin/run.sh create mode 100755 agent-analyer/bin/stop.sh diff --git a/.gitignore b/.gitignore index 07f590f..cf93773 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ *.jar *.war *.ear -javaagent.iml +*.iml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* diff --git a/agent-analyer/agent-analyzer.iml b/agent-analyer/agent-analyzer.iml deleted file mode 100644 index 591b2be..0000000 --- a/agent-analyer/agent-analyzer.iml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/agent-analyer/bin/README.md b/agent-analyer/bin/README.md new file mode 100644 index 0000000..3ac1bdc --- /dev/null +++ b/agent-analyer/bin/README.md @@ -0,0 +1,28 @@ +# agent-analyzer启动教程 + +## 启动服务 + +启动 agent-analyzer 有以下两种方式,请使用提供的脚本(`run.sh`)启动程序 + +### 使用 `run.sh` 启动程序 + +请使用如下命令启动服务:`sh run.sh [port] [jmx-port]` + ++ `port` 为访问 UIM API 的端口,如果不指定的话,默认为 **80** ++ `jmx-port` 为 JMX 远程监控端口。如果不指定的话,不会开启 + + +## 验证启动是否成功 + +在运行 shell 脚本后,静候一小段时间,在浏览器中键入以下地址,即可查看是否启动成功: + +`http://ip:port/` + + +## 停止服务 + +使用如下脚本停止服务,这样会停止所有的 `agent-analyzer` 服务。 + +```bash +sh stop.sh +``` \ No newline at end of file diff --git a/agent-analyer/bin/run.sh b/agent-analyer/bin/run.sh new file mode 100755 index 0000000..dec8295 --- /dev/null +++ b/agent-analyer/bin/run.sh @@ -0,0 +1,36 @@ +#!/bin/bash +DIR1="`dirname $BASH_SOURCE`" +CURRENT_PATH=`readlink -f "$DIR1"` + +APP_NAME="agent-analyzer.jar" + +APP_PORT=8080 +if [ -z "$1" ] +then + echo "没有指定程序端口,将使用默认端口 ${APP_PORT}" +else + APP_PORT=$1 +fi + + +echo "尝试获取本机 ip 地址" +IP_ADDR='127.0.0.1' +IP_ADDR=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') +echo "成功获取本机 ip 地址: $IP_ADDR" + +JAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx2g -Xmn256m" +JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC" +JAVA_OPT="${JAVA_OPT} -XX:+HeapDumpOnOutOfMemoryError" +if [ -z "$2" ] +then + echo "没有指定 JMX 远程端口,将无法进行远程监控" +else + JAVA_OPT="${JAVA_OPT} -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=${2} -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostn +ame=${IP_ADDR}" +fi + +echo "程序运行参数为:${JAVA_OPT}" +echo "程序运行端口为:${APP_PORT}" + +nohup java ${JAVA_OPT} -jar ${CURRENT_PATH}/${APP_NAME} --server.port=$APP_PORT > nohup_${APP_PORT}.out 2>&1 & + diff --git a/agent-analyer/bin/stop.sh b/agent-analyer/bin/stop.sh new file mode 100755 index 0000000..4f7473c --- /dev/null +++ b/agent-analyer/bin/stop.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# add comment +if [ -z "$1" ] +then + pid=`ps ax |grep -i 'agent-analyzer.jar' |grep java | grep -v grep | awk '{print $1}'` +else + pid=`ps ax |grep -i 'agent-analyzer.jar' |grep java | grep -i 'server.port='''${1}''''| grep -v grep | awk '{print $1}'` +fi + +if [ -z "$pid" ] ; then + echo "agent-analyzer is not running." + exit 0; +fi +echo "agent-analyzer(${pid}) is running." +echo "Send shutdown request to agent-analyzer(${pid})....." +kill -9 ${pid} +echo "Shutdown agent-analyzer(${pid}) success." + \ No newline at end of file diff --git a/agent-analyer/pom.xml b/agent-analyer/pom.xml index db7e3bc..16c3c8f 100644 --- a/agent-analyer/pom.xml +++ b/agent-analyer/pom.xml @@ -1,138 +1,138 @@ - - 4.0.0 - - com.wenshuo - agent-analyzer - 1.0.0 - war - - agent日志分析器 - - - UTF-8 - 1.8 - 2.6.7 - 1.2.83 - 2.15.1 - - - - - - - org.springframework.boot - spring-boot-dependencies - ${springboot.version} - pom - import - - - - - - - org.projectlombok - lombok - - - org.springframework.boot - spring-boot-starter-web - - - org.apache.commons - commons-lang3 - - - - - - - - - - com.alibaba - fastjson - ${fastjson.version} - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - - com.h2database - h2 - runtime - - - - commons-io - commons-io - ${commons-io.version} - - - commons-logging - commons-logging - 1.2 - compile - - - - - agent-analyzer - - - src/main/java - - **/*.xml - - - - ${basedir}/src/main/webapp - META-INF/resources - - **/** - - - - src/main/resources - false - - .gitkeep - - - ** - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - ${java.version} - ${java.version} - - - - org.springframework.boot - spring-boot-maven-plugin - ${springboot.version} - - - - repackage - - - - - com.wenshuo.agent.analyzer.AgentAnalyzer - true - - - - - + + 4.0.0 + + com.wenshuo + agent-analyzer + 1.0.0 + jar + + agent日志分析器 + + + UTF-8 + 1.8 + 2.6.7 + 1.2.83 + 2.15.1 + + + + + + + org.springframework.boot + spring-boot-dependencies + ${springboot.version} + pom + import + + + + + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.commons + commons-lang3 + + + + + + + + + + com.alibaba + fastjson + ${fastjson.version} + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + runtime + + + + commons-io + commons-io + ${commons-io.version} + + + commons-logging + commons-logging + 1.2 + compile + + + + + agent-analyzer + + + src/main/java + + **/*.xml + + + + ${basedir}/src/main/webapp + META-INF/resources + + **/** + + + + src/main/resources + false + + .gitkeep + + + ** + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.springframework.boot + spring-boot-maven-plugin + ${springboot.version} + + + + repackage + + + + + com.wenshuo.agent.analyzer.AgentAnalyzer + true + + + + + From c2b0951714bace80c47dfe0e9d094257adbf211f Mon Sep 17 00:00:00 2001 From: fanzhongwei Date: Wed, 11 Sep 2024 11:27:17 +0800 Subject: [PATCH 23/25] =?UTF-8?q?bugfix:=20=E5=A4=84=E7=90=86=E5=B7=A6?= =?UTF-8?q?=E4=BE=A7=E5=AF=BC=E8=88=AA=E5=86=85=E5=AE=B9=E8=BF=87=E5=A4=9A?= =?UTF-8?q?=E6=97=B6=E6=98=BE=E7=A4=BA=E4=B8=8D=E5=85=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/static/index.html | 1266 ++++++++--------- 1 file changed, 633 insertions(+), 633 deletions(-) diff --git a/agent-analyer/src/main/resources/static/index.html b/agent-analyer/src/main/resources/static/index.html index 1fa50c3..2d06738 100644 --- a/agent-analyer/src/main/resources/static/index.html +++ b/agent-analyer/src/main/resources/static/index.html @@ -1,634 +1,634 @@ - - - - - agent日志自动化工具 - - - - -
        -
        - - - -
        - - -
        - -
        -
        - - -
        -
        -
        - -
        - -
        - -
        - - - - - - - - - - -
        文件名大小状态操作
        -
        - - -
        - - -
        - - - -
        -
        -
        - -
        - - -
        - - -
        - - -
        - -
        - -
        - -
        - - -
        -
        -
        -
        - -
        -
        - - -
        -
        -
        - - - - - - - - - - + + + + + agent日志自动化工具 + + + + +
        +
        + + + +
        + + +
        + +
        +
        + + +
        +
        +
        + +
        + +
        + +
        + + + + + + + + + + +
        文件名大小状态操作
        +
        + + +
        + + +
        + + + +
        +
        +
        + +
        + + +
        + + +
        + + +
        + +
        + +
        + +
        + + +
        +
        +
        +
        + +
        +
        + + +
        +
        +
        + + + + + + + + + + \ No newline at end of file From fbab9fa50b8386c8ff17bb6af2f7f45f1f1394a6 Mon Sep 17 00:00:00 2001 From: fanzhongwei Date: Mon, 30 Sep 2024 11:47:19 +0800 Subject: [PATCH 24/25] =?UTF-8?q?bugfix:=20=E5=A4=84=E7=90=86=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=BC=82=E5=B8=B8=E6=97=B6=E6=97=A0=E6=B3=95=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=96=B9=E6=B3=95=E6=89=A7=E8=A1=8C=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wenshuo/agent/log/ExecuteLogUtils.java | 1 + .../AgentLogClassFileTransformer.java | 7 +-- .../com/wenshuo/agent/test/TestCtMethod.java | 49 ++++++++++++++++--- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/agent/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java b/agent/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java index 8ac2305..e74753d 100644 --- a/agent/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java +++ b/agent/src/main/java/com/wenshuo/agent/log/ExecuteLogUtils.java @@ -165,6 +165,7 @@ private static void logExecuteCounter(String className, String methodName, long int compression = 150; int factor = 5; md = new MergingDigest(compression, (factor + 1) * compression, compression); + md.add(executeTime); } methodCounterMap.put(methodName, new Object[] {1L, executeTime, md}); } else { diff --git a/agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java b/agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java index 6602f06..7b6a16b 100644 --- a/agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java +++ b/agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java @@ -59,6 +59,7 @@ private byte[] aopLog(ClassLoader loader, String className, byte[] byteCode) { } byteCode = aopLog(cc, className, byteCode); } catch (Exception ex) { + ex.printStackTrace(); log.error(ex.getMessage(), ex); } return byteCode; @@ -100,10 +101,10 @@ private void aopLog(String className, CtMethod m) throws CannotCompileException // 避免变量名重复 m.addLocalVariable("dingjsh_javaagent_elapsedTime", CtClass.longType); + m.insertAfter("dingjsh_javaagent_elapsedTime = " + timeMethodStr + " - dingjsh_javaagent_elapsedTime;" + + LOG_UTILS + ".log(" + aopClassName + ",\"" + m.getName() + + "\",(long)dingjsh_javaagent_elapsedTime" + ");", true); m.insertBefore("dingjsh_javaagent_elapsedTime = " + timeMethodStr + ";"); - m.insertAfter("dingjsh_javaagent_elapsedTime = " + timeMethodStr + " - dingjsh_javaagent_elapsedTime;"); - m.insertAfter(LOG_UTILS + ".log(" + aopClassName + ",\"" + m.getName() - + "\",(long)dingjsh_javaagent_elapsedTime" + ");"); } /** diff --git a/agent/src/test/java/com/wenshuo/agent/test/TestCtMethod.java b/agent/src/test/java/com/wenshuo/agent/test/TestCtMethod.java index 66ec31a..273b8bc 100644 --- a/agent/src/test/java/com/wenshuo/agent/test/TestCtMethod.java +++ b/agent/src/test/java/com/wenshuo/agent/test/TestCtMethod.java @@ -10,16 +10,12 @@ import com.tdunning.math.stats.Dist; import com.tdunning.math.stats.MergingDigest; import com.tdunning.math.stats.ScaleFunction; +import com.wenshuo.agent.ConfigUtils; +import com.wenshuo.agent.javassist.*; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import com.wenshuo.agent.javassist.ClassPool; -import com.wenshuo.agent.javassist.CtClass; -import com.wenshuo.agent.javassist.CtMethod; -import com.wenshuo.agent.javassist.Modifier; -import com.wenshuo.agent.javassist.NotFoundException; - public class TestCtMethod { private static List staticMethodNames = Arrays.asList("privateStatic","protectedStatic","publicStatic","defaultStatic"); @@ -53,6 +49,47 @@ static void defaultStatic(){ System.out.println("I'm default and static"); } + public static Object publicStaticWithResult(){ + String result = null; + System.out.println("I'm public and static"); + Random random = new Random(); + if(random.nextInt(100) < 50){ + result = new String("hello world:random.nextInt(100) < 50"); + return result; + } + result = new String("hello world:random.nextInt(100) >= 50"); + return result; + } + + @Test + @Ignore + public void asFinallyTest() throws Exception { + String className = "com.wenshuo.agent.test.TestCtMethod"; + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = pool.get(className); + + transformerMethod(className, ctClass.getDeclaredMethod("publicStatic")); + transformerMethod(className, ctClass.getDeclaredMethod("publicStaticWithResult")); + + ctClass.writeFile("/home/code/github/javaagent/agent/target"); + } + + private static void transformerMethod(String className, CtMethod m) throws CannotCompileException { + boolean isMethodStatic = Modifier.isStatic(m.getModifiers()); + String aopClassName = isMethodStatic ? "\"" + className + "\"" : "this.getClass().getName()"; + final String timeMethodStr + = ConfigUtils.isUsingNanoTime() ? "java.lang.System.nanoTime()" : "java.lang.System.currentTimeMillis" + + "()"; + String LOG_UTILS = "com.wenshuo.agent.log.ExecuteLogUtils"; + + // 避免变量名重复 + m.addLocalVariable("dingjsh_javaagent_elapsedTime", CtClass.longType); + m.insertAfter("dingjsh_javaagent_elapsedTime = " + timeMethodStr + " - dingjsh_javaagent_elapsedTime;" + + LOG_UTILS + ".log(" + aopClassName + ",\"" + m.getName() + + "\",(long)dingjsh_javaagent_elapsedTime" + ");", true); + m.insertBefore("dingjsh_javaagent_elapsedTime = " + timeMethodStr + ";"); + } + /** * 100个线程并发测试,每个线程往100个不同的Digest中添加5000000个随机数据[0, 100],并计算耗时
        From 60874a6bf279ea08dc298f9f85444d16f8ff3969 Mon Sep 17 00:00:00 2001 From: fanzhongwei Date: Mon, 30 Sep 2024 12:00:51 +0800 Subject: [PATCH 25/25] =?UTF-8?q?bugfix:=20javaagent=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AD=97=E8=8A=82=E7=A0=81=E5=A4=B1=E8=B4=A5=E6=97=B6=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E6=8E=A7=E5=88=B6=E5=8F=B0=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wenshuo/agent/transformer/AgentLogClassFileTransformer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java b/agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java index 7b6a16b..60cf216 100644 --- a/agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java +++ b/agent/src/main/java/com/wenshuo/agent/transformer/AgentLogClassFileTransformer.java @@ -59,7 +59,7 @@ private byte[] aopLog(ClassLoader loader, String className, byte[] byteCode) { } byteCode = aopLog(cc, className, byteCode); } catch (Exception ex) { - ex.printStackTrace(); + System.err.println("javaagent修改字节码失败,失败原因:" + ex.getMessage()); log.error(ex.getMessage(), ex); } return byteCode;