diff --git a/example-projects/jersey-example/.classpath b/example-projects/jersey-example/.classpath
new file mode 100644
index 00000000..f619a536
--- /dev/null
+++ b/example-projects/jersey-example/.classpath
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example-projects/jersey-example/.gitignore b/example-projects/jersey-example/.gitignore
new file mode 100644
index 00000000..b83d2226
--- /dev/null
+++ b/example-projects/jersey-example/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/example-projects/jersey-example/.project b/example-projects/jersey-example/.project
new file mode 100644
index 00000000..fdd46163
--- /dev/null
+++ b/example-projects/jersey-example/.project
@@ -0,0 +1,23 @@
+
+
+ jersey-example
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/example-projects/jersey-example/.settings/org.eclipse.core.resources.prefs b/example-projects/jersey-example/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 00000000..f9fe3459
--- /dev/null
+++ b/example-projects/jersey-example/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding/=UTF-8
diff --git a/example-projects/jersey-example/.settings/org.eclipse.jdt.core.prefs b/example-projects/jersey-example/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000..ec4300d5
--- /dev/null
+++ b/example-projects/jersey-example/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/example-projects/jersey-example/.settings/org.eclipse.m2e.core.prefs b/example-projects/jersey-example/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 00000000..f897a7f1
--- /dev/null
+++ b/example-projects/jersey-example/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/example-projects/jersey-example/README.md b/example-projects/jersey-example/README.md
new file mode 100644
index 00000000..aafd4f92
--- /dev/null
+++ b/example-projects/jersey-example/README.md
@@ -0,0 +1,7 @@
+# nginx jsersey example
+
+## build
+
+```bash
+mvn compile assembly:single -DskipTests
+```
\ No newline at end of file
diff --git a/example-projects/jersey-example/pom.xml b/example-projects/jersey-example/pom.xml
new file mode 100644
index 00000000..a0827443
--- /dev/null
+++ b/example-projects/jersey-example/pom.xml
@@ -0,0 +1,88 @@
+
+
+ 4.0.0
+
+ nginx-clojure
+ jersey-example
+ jar
+ 1.0
+ jersey-example
+
+
+
+
+ org.glassfish.jersey
+ jersey-bom
+ ${jersey.version}
+ pom
+ import
+
+
+
+
+
+
+ org.glassfish.jersey.containers
+ jersey-container-grizzly2-http
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-jackson
+
+
+ junit
+ junit
+ 4.9
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.5.1
+ true
+
+ 1.7
+ 1.7
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.2.1
+
+
+
+ java
+
+
+
+
+ nginx.clojure.jersey.example.Main
+
+
+
+ maven-assembly-plugin
+
+
+
+ nginx.clojure.jersey.example.Main
+
+
+
+ jar-with-dependencies
+
+
+
+
+
+
+
+ 2.25.1
+ UTF-8
+
+
diff --git a/example-projects/jersey-example/src/main/java/nginx/clojure/jersey/example/Main.java b/example-projects/jersey-example/src/main/java/nginx/clojure/jersey/example/Main.java
new file mode 100644
index 00000000..1d54f245
--- /dev/null
+++ b/example-projects/jersey-example/src/main/java/nginx/clojure/jersey/example/Main.java
@@ -0,0 +1,45 @@
+package nginx.clojure.jersey.example;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * Main class.
+ *
+ */
+public class Main {
+ // Base URI the Grizzly HTTP server will listen on
+ public static final String BASE_URI = "http://localhost:8087/myapp/";
+
+ /**
+ * Starts Grizzly HTTP server exposing JAX-RS resources defined in this application.
+ * @return Grizzly HTTP server.
+ */
+ public static HttpServer startServer() {
+ // create a resource config that scans for JAX-RS resources and providers
+ // in nginx.clojure.jersey.example package
+ final ResourceConfig rc = new ResourceConfig().packages("nginx.clojure.jersey.example");
+
+ // create and start a new instance of grizzly http server
+ // exposing the Jersey application at BASE_URI
+ return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
+ }
+
+ /**
+ * Main method.
+ * @param args
+ * @throws IOException
+ */
+ public static void main(String[] args) throws IOException {
+ final HttpServer server = startServer();
+ System.out.println(String.format("Jersey app started with WADL available at "
+ + "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
+ System.in.read();
+ server.stop();
+ }
+}
+
diff --git a/example-projects/jersey-example/src/main/java/nginx/clojure/jersey/example/MyResource.java b/example-projects/jersey-example/src/main/java/nginx/clojure/jersey/example/MyResource.java
new file mode 100644
index 00000000..e1413b91
--- /dev/null
+++ b/example-projects/jersey-example/src/main/java/nginx/clojure/jersey/example/MyResource.java
@@ -0,0 +1,32 @@
+package nginx.clojure.jersey.example;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Root resource (exposed at "myresource" path)
+ */
+@Path("myresource")
+public class MyResource {
+
+ /**
+ * Method handling HTTP GET requests. The returned object will be sent
+ * to the client as "text/plain" media type.
+ *
+ * @return String that will be returned as a text/plain response.
+ */
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public String getIt() {
+ return "Got it!";
+ }
+
+ @GET
+ @Path("/empty")
+ public Response empty() {
+ return Response.status(201).entity("").build();
+ }
+}
diff --git a/example-projects/jersey-example/src/test/java/nginx/clojure/jersey/example/MyResourceTest.java b/example-projects/jersey-example/src/test/java/nginx/clojure/jersey/example/MyResourceTest.java
new file mode 100644
index 00000000..e238af26
--- /dev/null
+++ b/example-projects/jersey-example/src/test/java/nginx/clojure/jersey/example/MyResourceTest.java
@@ -0,0 +1,48 @@
+package nginx.clojure.jersey.example;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+public class MyResourceTest {
+
+ private HttpServer server;
+ private WebTarget target;
+
+ @Before
+ public void setUp() throws Exception {
+ // start the server
+ server = Main.startServer();
+ // create the client
+ Client c = ClientBuilder.newClient();
+
+ // uncomment the following line if you want to enable
+ // support for JSON in the client (you also have to uncomment
+ // dependency on jersey-media-json module in pom.xml and Main.startServer())
+ // --
+ // c.configuration().enable(new org.glassfish.jersey.media.json.JsonJaxbFeature());
+
+ target = c.target(Main.BASE_URI);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ server.stop();
+ }
+
+ /**
+ * Test to see that the message "Got it!" is sent in the response.
+ */
+ @Test
+ public void testGetIt() {
+ String responseMsg = target.path("myresource").request().get(String.class);
+ assertEquals("Got it!", responseMsg);
+ }
+}
diff --git a/nginx-clojure-embed/configure-centos5-x64 b/nginx-clojure-embed/configure-centos5-x64
new file mode 100755
index 00000000..07842003
--- /dev/null
+++ b/nginx-clojure-embed/configure-centos5-x64
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+# Copyright (C) Zhang,Yuexiang (xfeep)
+
+
+NGINX_SRC=$NGINX_SRC
+NGINX_SRC=/home/who/build4embed/nginx-current
+NGINX_CLOJURE_SRC=$(pwd)/..
+NGINX_CLOJURE_EMBED_SRC=$(pwd)
+
+help(){
+ echo "[Usage]:" \
+ "env NGINX_SRC=nginx-src-path ./configure"
+ echo "[example]: env NGINX_SRC=/home/who/share/tmp/nginx-release-1.8.0 ./configure"
+}
+
+if ! [ -f $NGINX_SRC/src/core/nginx.c ]; then
+ echo "[ERROR]:nginx source not found:\$NGINX_SRC=\"$NGINX_SRC\""
+ help
+ exit 1
+fi
+
+if ! [ -f $NGINX_CLOJURE_SRC/src/c/ngx_http_clojure_mem.c ]; then
+ echo "[ERROR]:nginx-clojure source not found:\$NGINX_CLOJURE_SRC=\"$NGINX_CLOJURE_SRC\""
+ help
+ exit 1
+fi
+
+
+
+##check jdk
+if ! type javac; then
+ echo "javac not found, please put it in your PATH"
+ exit 1
+fi
+
+if ! type java; then
+ echo "java not found, please put it in your PATH"
+ exit 1
+fi
+
+mkdir /tmp/nc-DiscoverJvm
+javac $NGINX_CLOJURE_SRC/src/java/nginx/clojure/DiscoverJvm.java -d /tmp/nc-DiscoverJvm
+
+if [ -z $JNI_INCS ]; then
+ JNI_INCS=`java -classpath /tmp/nc-DiscoverJvm nginx.clojure.DiscoverJvm getJniIncludes`;
+fi
+
+nginx_clojure_embed_ext=`java -classpath /tmp/nc-DiscoverJvm nginx.clojure.DiscoverJvm detectOSArchExt`
+nginx_clojure_embed_ext="-$nginx_clojure_embed_ext"
+rm -rf /tmp/nc-DiscoverJvm
+
+cd $NGINX_SRC
+if ! [ -f src/core/nginx.c.org ]; then
+ cp src/core/nginx.c src/core/nginx.c.org
+# sed -e ':a' -e 'N' -e '$!ba' -e 's/static[ ]ngx_int_t ngx_save_argv/ngx_int_t ngx_save_argv/g' src/core/nginx.c > src/core/nginx.c-new
+# sed -e ':a' -e 'N' -e '$!ba' -e 's/static[ ]ngx_int_t\s*\n*ngx_save_argv/ngx_int_t ngx_save_argv/g' src/core/nginx.c-new > src/core/nginx.c
+ sed -e ':a' -e 'N' -e '$!ba' -e 's/static[ ]ngx_int_t/ngx_int_t/g' src/core/nginx.c > src/core/nginx.c-new
+ mv src/core/nginx.c-new src/core/nginx.c
+fi
+
+set -- --prefix= \
+ --conf-path=conf/nginx.conf --pid-path=logs/nginx.pid \
+ --http-log-path=logs/access.log --error-log-path=logs/error.log \
+ --sbin-path=nginx --http-client-body-temp-path=temp/client_body_temp \
+ --http-proxy-temp-path=temp/proxy_temp \
+ --http-proxy-temp-path=temp/proxy_temp \
+ --without-http_ssi_module \
+ --without-http_userid_module \
+ --without-http_geo_module \
+ --without-http_split_clients_module \
+ --without-http_referer_module \
+ --without-http_fastcgi_module \
+ --without-http_uwsgi_module \
+ --without-http_scgi_module \
+ --without-http_memcached_module \
+ --without-http_empty_gif_module \
+ --without-http_upstream_hash_module \
+ --without-http_upstream_ip_hash_module \
+ --without-http_upstream_least_conn_module \
+ --without-http_upstream_keepalive_module \
+ --without-http-cache \
+ --without-mail_pop3_module \
+ --without-mail_imap_module \
+ --without-mail_smtp_module \
+ --with-debug \
+ --add-module=${NGINX_CLOJURE_SRC}/src/c \
+ --add-module=${NGINX_CLOJURE_EMBED_SRC}/src/c \
+ --with-pcre=../pcre-8.40 \
+ --with-pcre-opt=-fPIC
+
+if [ -f "auto/configure" ]; then
+ . auto/configure
+else
+ . ./configure
+fi
+
+
+
+nginx_clojure_embed_shared_lib=nginx-clojure-embed${nginx_clojure_embed_ext}
+
+cat << END >> objs/Makefile
+
+
+$NGX_OBJS${ngx_dirsep}${nginx_clojure_embed_shared_lib}: $ngx_deps$ngx_spacer
+ \$(LINK) ${ngx_long_start}${ngx_binout}$NGX_OBJS${ngx_dirsep}${nginx_clojure_embed_shared_lib}$ngx_long_cont$ngx_objs$ngx_libs$ngx_link
+ $ngx_rcc
+${ngx_long_end}
+
+embed: $NGX_OBJS${ngx_dirsep}${nginx_clojure_embed_shared_lib}
+ mkdir -p "$NGINX_CLOJURE_EMBED_SRC/res/slib"
+ cp $NGX_OBJS${ngx_dirsep}${nginx_clojure_embed_shared_lib} $NGINX_CLOJURE_EMBED_SRC/res/slib
+ if type strip; then \
+ strip -x -S $NGINX_CLOJURE_EMBED_SRC/res/slib/${nginx_clojure_embed_shared_lib}; \
+ fi
+ cd $NGINX_CLOJURE_EMBED_SRC; \
+ echo "finish build nginx-clojure embed"
+END
+
+echo "creating $NGINX_CLOJURE_EMBED_SRC\Makefile"
+cd $NGINX_CLOJURE_EMBED_SRC
+cat << END > Makefile
+default:
+ cd $NGINX_SRC; \
+ \$(MAKE) -f objs/Makefile embed
+ cd $NGINX_CLOJURE_EMBED_SRC
+
+clean:
+ cd $NGINX_SRC; \
+ \$(MAKE) clean
+ cd $NGINX_CLOJURE_EMBED_SRC; \
+ rm $NGINX_CLOJURE_EMBED_SRC/res/slib/${nginx_clojure_embed_shared_lib} && \
+ rm Makefile
+END
+
+echo "done"
+echo "please try make to compile shared library of nginx-clojure-embed"
+echo "[if nginx version < 1.11.2] make sure use './config -fPIC && make' to make openssl"
+echo "[if nginx version < 1.11.2] modify nginx/objs/Makefile replace -lcrypt with ../openssl-1.0.1e/libcrypto.a"
\ No newline at end of file
diff --git a/nginx-clojure-embed/configure-win32 b/nginx-clojure-embed/configure-win32
new file mode 100644
index 00000000..6ba34bb9
--- /dev/null
+++ b/nginx-clojure-embed/configure-win32
@@ -0,0 +1,147 @@
+#!/bin/sh
+
+# Copyright (C) Zhang,Yuexiang (xfeep)
+
+NGINX_SRC=c:/mingw/msys/1.0/home/myadmin/build-for-embed/nginx-1.12.0
+NGINX_CLOJURE_SRC=c:/mingw/msys/1.0/home/myadmin/build-for-embed/nginx-clojure
+NGINX_CLOJURE_EMBED_SRC=c:/mingw/msys/1.0/home/myadmin/build-for-embed/nginx-clojure/nginx-clojure-embed
+
+help(){
+ echo "[Usage]:" \
+ "env NGINX_SRC=nginx-src-path ./configure"
+ echo "[example]: env NGINX_SRC=/home/who/share/tmp/nginx-release-1.8.0 ./configure"
+}
+
+if ! [ -f $NGINX_SRC/src/core/nginx.c ]; then
+ echo "[ERROR]:nginx source not found:\$NGINX_SRC=\"$NGINX_SRC\""
+ help
+ exit 1
+fi
+
+if ! [ -f $NGINX_CLOJURE_SRC/src/c/ngx_http_clojure_mem.c ]; then
+ echo "[ERROR]:nginx-clojure source not found:\$NGINX_CLOJURE_SRC=\"$NGINX_CLOJURE_SRC\""
+ help
+ exit 1
+fi
+
+
+
+##check jdk
+if ! type javac; then
+ echo "javac not found, please put it in your PATH"
+ exit 1
+fi
+
+if ! type java; then
+ echo "java not found, please put it in your PATH"
+ exit 1
+fi
+
+javac $NGINX_CLOJURE_SRC/src/java/nginx/clojure/DiscoverJvm.java
+
+if [ -z $JNI_INCS ]; then
+ JNI_INCS=`java -classpath $NGINX_CLOJURE_SRC/src/java nginx.clojure.DiscoverJvm getJniIncludes`;
+fi
+
+nginx_clojure_embed_ext=`java -classpath $NGINX_CLOJURE_SRC/src/java nginx.clojure.DiscoverJvm detectOSArchExt`
+nginx_clojure_embed_ext="-$nginx_clojure_embed_ext"
+rm $NGINX_CLOJURE_SRC/src/java/nginx/clojure/DiscoverJvm.class
+
+cd $NGINX_SRC
+if ! [ -f src/core/nginx.c.org ]; then
+ cp src/core/nginx.c src/core/nginx.c.org
+# sed -e ':a' -e 'N' -e '$!ba' -e 's/static[ ]ngx_int_t ngx_save_argv/ngx_int_t ngx_save_argv/g' src/core/nginx.c > src/core/nginx.c-new
+# sed -e ':a' -e 'N' -e '$!ba' -e 's/static[ ]ngx_int_t\s*\n*ngx_save_argv/ngx_int_t ngx_save_argv/g' src/core/nginx.c-new > src/core/nginx.c
+ sed -e ':a' -e 'N' -e '$!ba' -e 's/static[ ]ngx_int_t/ngx_int_t/g' src/core/nginx.c > src/core/nginx.c-new
+ mv src/core/nginx.c-new src/core/nginx.c
+fi
+
+set -- --prefix= \
+ --with-cc=cl \
+ --builddir=objs \
+ --sbin-path=nginx.exe \
+ --with-cc-opt=-DFD_SETSIZE=4096 \
+ --with-select_module \
+ --conf-path=conf/nginx.conf --pid-path=logs/nginx.pid \
+ --http-log-path=logs/access.log --error-log-path=logs/error.log \
+ --sbin-path=nginx --http-client-body-temp-path=temp/client_body_temp \
+ --http-proxy-temp-path=temp/proxy_temp \
+ --http-proxy-temp-path=temp/proxy_temp \
+ --without-http_ssi_module \
+ --without-http_userid_module \
+ --without-http_geo_module \
+ --without-http_split_clients_module \
+ --without-http_referer_module \
+ --without-http_fastcgi_module \
+ --without-http_uwsgi_module \
+ --without-http_scgi_module \
+ --without-http_memcached_module \
+ --without-http_empty_gif_module \
+ --without-http_upstream_hash_module \
+ --without-http_upstream_ip_hash_module \
+ --without-http_upstream_least_conn_module \
+ --without-http_upstream_keepalive_module \
+ --without-http-cache \
+ --without-mail_pop3_module \
+ --without-mail_imap_module \
+ --without-mail_smtp_module \
+ --with-debug \
+ --add-module=${NGINX_CLOJURE_SRC}/src/c \
+ --add-module=${NGINX_CLOJURE_EMBED_SRC}/src/c \
+ --with-pcre=../pcre-8.40 \
+ --with-zlib=../zlib-1.2.11 \
+
+## --with-sha1=C:/MinGW/msys/1.0/home/myadmin/nginx-current/objs/lib/openssl-1.0.1e/crypto/sha \
+## --with-cc-opt="-I C:/MinGW/msys/1.0/home/myadmin/nginx-current/objs/lib/openssl-1.0.1e/openssl/include"
+## --with-http_ssl_module \
+## --with-openssl=C:/MinGW/msys/1.0/home/myadmin/nginx-current/objs/lib/openssl-1.0.1e
+
+
+if [ -f "auto/configure" ]; then
+ . auto/configure
+else
+ . ./configure
+fi
+
+
+
+nginx_clojure_embed_shared_lib=nginx-clojure-embed${nginx_clojure_embed_ext}
+
+cat << END >> objs/Makefile
+
+
+$NGX_OBJS${ngx_dirsep}${nginx_clojure_embed_shared_lib}: $ngx_deps$ngx_spacer
+ \$(LINK) ${ngx_long_start}${ngx_binout}$NGX_OBJS${ngx_dirsep}${nginx_clojure_embed_shared_lib}$ngx_long_cont$ngx_objs$ngx_libs$ngx_link
+ $ngx_rcc
+${ngx_long_end}
+
+embed: $NGX_OBJS${ngx_dirsep}${nginx_clojure_embed_shared_lib}
+ mkdir -p "$NGINX_CLOJURE_EMBED_SRC/res/slib"
+ cp $NGX_OBJS${ngx_dirsep}${nginx_clojure_embed_shared_lib} $NGINX_CLOJURE_EMBED_SRC/res/slib
+ if type strip; then \
+ strip -x -S $NGINX_CLOJURE_EMBED_SRC/res/slib/${nginx_clojure_embed_shared_lib}; \
+ fi
+ cd $NGINX_CLOJURE_EMBED_SRC; \
+ echo "finish build nginx-clojure embed"
+END
+
+echo "creating $NGINX_CLOJURE_EMBED_SRC\Makefile"
+cd $NGINX_CLOJURE_EMBED_SRC
+cat << END > Makefile
+default:
+ cd $NGINX_SRC; \
+ \$(MAKE) -f objs/Makefile embed
+ cd $NGINX_CLOJURE_EMBED_SRC
+
+clean:
+ cd $NGINX_SRC; \
+ \$(MAKE) clean
+ cd $NGINX_CLOJURE_EMBED_SRC; \
+ rm $NGINX_CLOJURE_EMBED_SRC/res/slib/${nginx_clojure_embed_shared_lib} && \
+ rm Makefile
+END
+
+echo "done"
+echo "please try make to compile shared library of nginx-clojure-embed"
+echo "please modify $NGINX_SRC/auto/lib/sha1/makefile.msvc to add openssl sha headers"
+echo "CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) -D L_ENDIAN -I C:/MinGW/msys/1.0/home/myadmin/nginx-current/objs/lib/openssl-1.0.1e/openssl/include -I C:/MinGW/msys/1.0/home/myadmin/nginx-current/objs/lib/openssl-1.0.1e/crypto"
diff --git a/nginx-clojure-embed/macos-issue b/nginx-clojure-embed/macos-issue
new file mode 100644
index 00000000..60466dc6
--- /dev/null
+++ b/nginx-clojure-embed/macos-issue
@@ -0,0 +1,11 @@
+
+for ncs in ngx_http_clojure_mem ngx_http_clojure_socket ngx_http_clojure_shared_map_tinymap ngx_http_clojure_shared_map_hashmap;
+do
+gcc -c -fpic -fvisibility=hidden -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include" -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include/darwin" -I "/Users/whomac/Dev/build-for-embed/nginx-clojure/nginx-clojure-embed/../src/c" -DNGX_CLOJURE_BE_SILENT_WITHOUT_JVM -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include" -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include/darwin" -pipe -O -Wall -g -I src/core -I src/event -I src/event/modules -I src/os/unix -I ../pcre-8.40 -I objs -I src/http -I src/http/modules -I /Users/whomac/Dev/build-for-embed/nginx-clojure/nginx-clojure-embed/../src/c \
+ -o objs/addon/c/${ncs}.o \
+ /Users/whomac/Dev/build-for-embed/nginx-clojure/nginx-clojure-embed/../src/c/${ncs}.c
+done
+
+gcc -c -fpic -fvisibility=hidden -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include" -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include/darwin" -I "/Users/whomac/Dev/build-for-embed/nginx-clojure/nginx-clojure-embed/../src/c" -DNGX_CLOJURE_BE_SILENT_WITHOUT_JVM -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include" -I "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include/darwin" -pipe -O -Wall -g -I src/core -I src/event -I src/event/modules -I src/os/unix -I ../pcre-8.40 -I objs -I src/http -I src/http/modules -I /Users/whomac/Dev/build-for-embed/nginx-clojure/nginx-clojure-embed/../src/c \
+ -o objs/addon/c/ngx_http_clojure_embed.o \
+ /Users/whomac/Dev/build-for-embed/nginx-clojure/nginx-clojure-embed/src/c/ngx_http_clojure_embed.c
diff --git a/project.clj b/project.clj
index 7cb6c4cb..71a5a2e6 100644
--- a/project.clj
+++ b/project.clj
@@ -1,4 +1,4 @@
-(defproject nginx-clojure/nginx-clojure "0.4.5"
+(defproject nginx-clojure/nginx-clojure "0.5.0"
:description "Nginx module for clojure or groovy or java programming"
:url "https://github.com/nginx-clojure/nginx-clojure"
:license {:name "BSD 3-Clause license"
@@ -62,9 +62,9 @@
[stylefruits/gniazdo "0.4.0"]
]}
:unittest {
- :jvm-opts ["-javaagent:target/nginx-clojure-0.4.4.jar=mb"
+ :jvm-opts ["-javaagent:target/nginx-clojure-0.5.0.jar=mb"
"-Dnginx.clojure.wave.udfs=pure-clj.txt,compojure.txt,compojure-http-clj.txt"
- "-Xbootclasspath/a:target/nginx-clojure-0.4.4.jar"]
+ "-Xbootclasspath/a:target/nginx-clojure-0.5.0.jar"]
:junit-options {:fork "on"}
:java-source-paths ["test/java" "test/clojure"]
:test-paths ["src/test/clojure"]
diff --git a/src/clojure/nginx/clojure/core.clj b/src/clojure/nginx/clojure/core.clj
index 23eac42a..d2eb1789 100644
--- a/src/clojure/nginx/clojure/core.clj
+++ b/src/clojure/nginx/clojure/core.clj
@@ -195,7 +195,7 @@
(.addListener ch ch (proxy [WholeMessageAdapter] [max-message-size]
(onOpen [c] (if on-open (on-open c)))
(onWholeTextMessage [c msg] (if on-message (on-message c msg)))
- (onWholeBiniaryMessage [c msg rem?] (if on-message (on-message c msg)))
+ (onWholeBiniaryMessage [c msg] (if on-message (on-message c msg)))
(onClose ;([c] (if on-close (on-close c "0")))
([c status reason] (if on-close (on-close c (str status ":" reason)))))
(onError [c status] (if on-error (on-error c (NginxClojureAsynSocket/errorCodeToString status)))))))
diff --git a/src/java/nginx/clojure/NginxClojureRT.java b/src/java/nginx/clojure/NginxClojureRT.java
index 36ba5fc7..7a2d70d6 100644
--- a/src/java/nginx/clojure/NginxClojureRT.java
+++ b/src/java/nginx/clojure/NginxClojureRT.java
@@ -8,7 +8,6 @@
import java.io.IOException;
import java.lang.management.ManagementFactory;
-import java.lang.reflect.Field;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
@@ -556,16 +555,7 @@ public static void initStringAddrMapsByNativeAddr(Map map, long ad
private static synchronized void initMemIndex(long idxpt) {
getLog();
- initUnsafe();
-
- //hack mysql jdbc driver to keep from creating connections by reflective invoking the constructor
- try {
- Class mysqljdbcUtilClz = Thread.currentThread().getContextClassLoader().loadClass("com.mysql.jdbc.Util");
- Field isJdbc4Field = mysqljdbcUtilClz.getDeclaredField("isJdbc4");
- isJdbc4Field.setAccessible(true);
- isJdbc4Field.set(null, false);
- } catch (Throwable e) {
- }
+ initUnsafe();
NGINX_MAIN_THREAD = Thread.currentThread();
diff --git a/src/java/nginx/clojure/asm/AnnotationVisitor.java b/src/java/nginx/clojure/asm/AnnotationVisitor.java
index 94b298d1..f128cc97 100644
--- a/src/java/nginx/clojure/asm/AnnotationVisitor.java
+++ b/src/java/nginx/clojure/asm/AnnotationVisitor.java
@@ -41,7 +41,7 @@ public abstract class AnnotationVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
- * must be one of {@link Opcodes#ASM4}.
+ * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
*/
protected final int api;
@@ -56,7 +56,7 @@ public abstract class AnnotationVisitor {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
*/
public AnnotationVisitor(final int api) {
this(api, null);
@@ -67,13 +67,13 @@ public AnnotationVisitor(final int api) {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param av
* the annotation visitor to which this visitor must delegate
* method calls. May be null.
*/
public AnnotationVisitor(final int api, final AnnotationVisitor av) {
- if (api != Opcodes.ASM4) {
+ if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
throw new IllegalArgumentException();
}
this.api = api;
@@ -89,7 +89,7 @@ public AnnotationVisitor(final int api, final AnnotationVisitor av) {
* the actual value, whose type must be {@link Byte},
* {@link Boolean}, {@link Character}, {@link Short},
* {@link Integer} , {@link Long}, {@link Float}, {@link Double},
- * {@link String} or {@link Type} or OBJECT or ARRAY sort. This
+ * {@link String} or {@link Type} of OBJECT or ARRAY sort. This
* value can also be an array of byte, boolean, short, char, int,
* long, float or double values (this is equivalent to using
* {@link #visitArray visitArray} and visiting each array element
diff --git a/src/java/nginx/clojure/asm/AnnotationWriter.java b/src/java/nginx/clojure/asm/AnnotationWriter.java
index 67827fa1..de1b472c 100644
--- a/src/java/nginx/clojure/asm/AnnotationWriter.java
+++ b/src/java/nginx/clojure/asm/AnnotationWriter.java
@@ -104,7 +104,7 @@ final class AnnotationWriter extends AnnotationVisitor {
*/
AnnotationWriter(final ClassWriter cw, final boolean named,
final ByteVector bv, final ByteVector parent, final int offset) {
- super(Opcodes.ASM4);
+ super(Opcodes.ASM5);
this.cw = cw;
this.named = named;
this.bv = bv;
@@ -315,4 +315,57 @@ static void put(final AnnotationWriter[] panns, final int off,
}
}
}
+
+ /**
+ * Puts the given type reference and type path into the given bytevector.
+ * LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported.
+ *
+ * @param typeRef
+ * a reference to the annotated type. See {@link TypeReference}.
+ * @param typePath
+ * the path to the annotated type argument, wildcard bound, array
+ * element type, or static inner type within 'typeRef'. May be
+ * null if the annotation targets 'typeRef' as a whole.
+ * @param out
+ * where the type reference and type path must be put.
+ */
+ static void putTarget(int typeRef, TypePath typePath, ByteVector out) {
+ switch (typeRef >>> 24) {
+ case 0x00: // CLASS_TYPE_PARAMETER
+ case 0x01: // METHOD_TYPE_PARAMETER
+ case 0x16: // METHOD_FORMAL_PARAMETER
+ out.putShort(typeRef >>> 16);
+ break;
+ case 0x13: // FIELD
+ case 0x14: // METHOD_RETURN
+ case 0x15: // METHOD_RECEIVER
+ out.putByte(typeRef >>> 24);
+ break;
+ case 0x47: // CAST
+ case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
+ case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT
+ case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
+ case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT
+ out.putInt(typeRef);
+ break;
+ // case 0x10: // CLASS_EXTENDS
+ // case 0x11: // CLASS_TYPE_PARAMETER_BOUND
+ // case 0x12: // METHOD_TYPE_PARAMETER_BOUND
+ // case 0x17: // THROWS
+ // case 0x42: // EXCEPTION_PARAMETER
+ // case 0x43: // INSTANCEOF
+ // case 0x44: // NEW
+ // case 0x45: // CONSTRUCTOR_REFERENCE
+ // case 0x46: // METHOD_REFERENCE
+ default:
+ out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8);
+ break;
+ }
+ if (typePath == null) {
+ out.putByte(0);
+ } else {
+ int length = typePath.b[typePath.offset] * 2 + 1;
+ out.putByteArray(typePath.b, typePath.offset, length);
+ }
+ }
}
diff --git a/src/java/nginx/clojure/asm/ByteVector.java b/src/java/nginx/clojure/asm/ByteVector.java
index 2eaf5c9d..21d7da8a 100644
--- a/src/java/nginx/clojure/asm/ByteVector.java
+++ b/src/java/nginx/clojure/asm/ByteVector.java
@@ -230,41 +230,68 @@ public ByteVector putUTF8(final String s) {
if (c >= '\001' && c <= '\177') {
data[len++] = (byte) c;
} else {
- int byteLength = i;
- for (int j = i; j < charLength; ++j) {
- c = s.charAt(j);
- if (c >= '\001' && c <= '\177') {
- byteLength++;
- } else if (c > '\u07FF') {
- byteLength += 3;
- } else {
- byteLength += 2;
- }
- }
- if (byteLength > 65535) {
- throw new IllegalArgumentException();
- }
- data[length] = (byte) (byteLength >>> 8);
- data[length + 1] = (byte) byteLength;
- if (length + 2 + byteLength > data.length) {
- length = len;
- enlarge(2 + byteLength);
- data = this.data;
- }
- for (int j = i; j < charLength; ++j) {
- c = s.charAt(j);
- if (c >= '\001' && c <= '\177') {
- data[len++] = (byte) c;
- } else if (c > '\u07FF') {
- data[len++] = (byte) (0xE0 | c >> 12 & 0xF);
- data[len++] = (byte) (0x80 | c >> 6 & 0x3F);
- data[len++] = (byte) (0x80 | c & 0x3F);
- } else {
- data[len++] = (byte) (0xC0 | c >> 6 & 0x1F);
- data[len++] = (byte) (0x80 | c & 0x3F);
- }
- }
- break;
+ length = len;
+ return encodeUTF8(s, i, 65535);
+ }
+ }
+ length = len;
+ return this;
+ }
+
+ /**
+ * Puts an UTF8 string into this byte vector. The byte vector is
+ * automatically enlarged if necessary. The string length is encoded in two
+ * bytes before the encoded characters, if there is space for that (i.e. if
+ * this.length - i - 2 >= 0).
+ *
+ * @param s
+ * the String to encode.
+ * @param i
+ * the index of the first character to encode. The previous
+ * characters are supposed to have already been encoded, using
+ * only one byte per character.
+ * @param maxByteLength
+ * the maximum byte length of the encoded string, including the
+ * already encoded characters.
+ * @return this byte vector.
+ */
+ ByteVector encodeUTF8(final String s, int i, int maxByteLength) {
+ int charLength = s.length();
+ int byteLength = i;
+ char c;
+ for (int j = i; j < charLength; ++j) {
+ c = s.charAt(j);
+ if (c >= '\001' && c <= '\177') {
+ byteLength++;
+ } else if (c > '\u07FF') {
+ byteLength += 3;
+ } else {
+ byteLength += 2;
+ }
+ }
+ if (byteLength > maxByteLength) {
+ throw new IllegalArgumentException();
+ }
+ int start = length - i - 2;
+ if (start >= 0) {
+ data[start] = (byte) (byteLength >>> 8);
+ data[start + 1] = (byte) byteLength;
+ }
+ if (length + byteLength - i > data.length) {
+ enlarge(byteLength - i);
+ }
+ int len = length;
+ for (int j = i; j < charLength; ++j) {
+ c = s.charAt(j);
+ if (c >= '\001' && c <= '\177') {
+ data[len++] = (byte) c;
+ } else if (c > '\u07FF') {
+ data[len++] = (byte) (0xE0 | c >> 12 & 0xF);
+ data[len++] = (byte) (0x80 | c >> 6 & 0x3F);
+ data[len++] = (byte) (0x80 | c & 0x3F);
+ } else {
+ data[len++] = (byte) (0xC0 | c >> 6 & 0x1F);
+ data[len++] = (byte) (0x80 | c & 0x3F);
}
}
length = len;
diff --git a/src/java/nginx/clojure/asm/ClassReader.java b/src/java/nginx/clojure/asm/ClassReader.java
index 96a49860..e2bc182d 100644
--- a/src/java/nginx/clojure/asm/ClassReader.java
+++ b/src/java/nginx/clojure/asm/ClassReader.java
@@ -104,6 +104,21 @@ public class ClassReader {
*/
public static final int EXPAND_FRAMES = 8;
+ /**
+ * Flag to expand the ASM pseudo instructions into an equivalent sequence of
+ * standard bytecode instructions. When resolving a forward jump it may
+ * happen that the signed 2 bytes offset reserved for it is not sufficient
+ * to store the bytecode offset. In this case the jump instruction is
+ * replaced with a temporary ASM pseudo instruction using an unsigned 2
+ * bytes offset (see Label#resolve). This internal flag is used to re-read
+ * classes containing such instructions, in order to replace them with
+ * standard instructions. In addition, when this flag is used, GOTO_W and
+ * JSR_W are not converted into GOTO and JSR, to make sure that
+ * infinite loops where a GOTO_W is replaced with a GOTO in ClassReader and
+ * converted back to a GOTO_W in ClassWriter cannot occur.
+ */
+ static final int EXPAND_ASM_INSNS = 256;
+
/**
* The class to be parsed. The content of this array must not be
* modified. This field is intended for {@link Attribute} sub classes, and
@@ -557,6 +572,8 @@ public void accept(final ClassVisitor classVisitor,
String enclosingDesc = null;
int anns = 0;
int ianns = 0;
+ int tanns = 0;
+ int itanns = 0;
int innerClasses = 0;
Attribute attributes = null;
@@ -581,6 +598,9 @@ public void accept(final ClassVisitor classVisitor,
} else if (ANNOTATIONS
&& "RuntimeVisibleAnnotations".equals(attrName)) {
anns = u + 8;
+ } else if (ANNOTATIONS
+ && "RuntimeVisibleTypeAnnotations".equals(attrName)) {
+ tanns = u + 8;
} else if ("Deprecated".equals(attrName)) {
access |= Opcodes.ACC_DEPRECATED;
} else if ("Synthetic".equals(attrName)) {
@@ -592,6 +612,9 @@ public void accept(final ClassVisitor classVisitor,
} else if (ANNOTATIONS
&& "RuntimeInvisibleAnnotations".equals(attrName)) {
ianns = u + 8;
+ } else if (ANNOTATIONS
+ && "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
+ itanns = u + 8;
} else if ("BootstrapMethods".equals(attrName)) {
int[] bootstrapMethods = new int[readUnsignedShort(u + 8)];
for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) {
@@ -626,7 +649,7 @@ public void accept(final ClassVisitor classVisitor,
enclosingDesc);
}
- // visits the class annotations
+ // visits the class annotations and type annotations
if (ANNOTATIONS && anns != 0) {
for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
v = readAnnotationValues(v + 2, c, true,
@@ -639,6 +662,22 @@ public void accept(final ClassVisitor classVisitor,
classVisitor.visitAnnotation(readUTF8(v, c), false));
}
}
+ if (ANNOTATIONS && tanns != 0) {
+ for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
+ v = readAnnotationTarget(context, v);
+ v = readAnnotationValues(v + 2, c, true,
+ classVisitor.visitTypeAnnotation(context.typeRef,
+ context.typePath, readUTF8(v, c), true));
+ }
+ }
+ if (ANNOTATIONS && itanns != 0) {
+ for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
+ v = readAnnotationTarget(context, v);
+ v = readAnnotationValues(v + 2, c, true,
+ classVisitor.visitTypeAnnotation(context.typeRef,
+ context.typePath, readUTF8(v, c), false));
+ }
+ }
// visits the attributes
while (attributes != null) {
@@ -697,6 +736,8 @@ private int readField(final ClassVisitor classVisitor,
String signature = null;
int anns = 0;
int ianns = 0;
+ int tanns = 0;
+ int itanns = 0;
Object value = null;
Attribute attributes = null;
@@ -717,9 +758,15 @@ private int readField(final ClassVisitor classVisitor,
} else if (ANNOTATIONS
&& "RuntimeVisibleAnnotations".equals(attrName)) {
anns = u + 8;
+ } else if (ANNOTATIONS
+ && "RuntimeVisibleTypeAnnotations".equals(attrName)) {
+ tanns = u + 8;
} else if (ANNOTATIONS
&& "RuntimeInvisibleAnnotations".equals(attrName)) {
ianns = u + 8;
+ } else if (ANNOTATIONS
+ && "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
+ itanns = u + 8;
} else {
Attribute attr = readAttribute(context.attrs, attrName, u + 8,
readInt(u + 4), c, -1, null);
@@ -739,7 +786,7 @@ private int readField(final ClassVisitor classVisitor,
return u;
}
- // visits the field annotations
+ // visits the field annotations and type annotations
if (ANNOTATIONS && anns != 0) {
for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
v = readAnnotationValues(v + 2, c, true,
@@ -752,6 +799,22 @@ private int readField(final ClassVisitor classVisitor,
fv.visitAnnotation(readUTF8(v, c), false));
}
}
+ if (ANNOTATIONS && tanns != 0) {
+ for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
+ v = readAnnotationTarget(context, v);
+ v = readAnnotationValues(v + 2, c, true,
+ fv.visitTypeAnnotation(context.typeRef,
+ context.typePath, readUTF8(v, c), true));
+ }
+ }
+ if (ANNOTATIONS && itanns != 0) {
+ for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
+ v = readAnnotationTarget(context, v);
+ v = readAnnotationValues(v + 2, c, true,
+ fv.visitTypeAnnotation(context.typeRef,
+ context.typePath, readUTF8(v, c), false));
+ }
+ }
// visits the field attributes
while (attributes != null) {
@@ -782,9 +845,9 @@ private int readMethod(final ClassVisitor classVisitor,
final Context context, int u) {
// reads the method declaration
char[] c = context.buffer;
- int access = readUnsignedShort(u);
- String name = readUTF8(u + 2, c);
- String desc = readUTF8(u + 4, c);
+ context.access = readUnsignedShort(u);
+ context.name = readUTF8(u + 2, c);
+ context.desc = readUTF8(u + 4, c);
u += 6;
// reads the method attributes
@@ -792,8 +855,11 @@ private int readMethod(final ClassVisitor classVisitor,
int exception = 0;
String[] exceptions = null;
String signature = null;
+ int methodParameters = 0;
int anns = 0;
int ianns = 0;
+ int tanns = 0;
+ int itanns = 0;
int dann = 0;
int mpanns = 0;
int impanns = 0;
@@ -818,24 +884,32 @@ private int readMethod(final ClassVisitor classVisitor,
} else if (SIGNATURES && "Signature".equals(attrName)) {
signature = readUTF8(u + 8, c);
} else if ("Deprecated".equals(attrName)) {
- access |= Opcodes.ACC_DEPRECATED;
+ context.access |= Opcodes.ACC_DEPRECATED;
} else if (ANNOTATIONS
&& "RuntimeVisibleAnnotations".equals(attrName)) {
anns = u + 8;
+ } else if (ANNOTATIONS
+ && "RuntimeVisibleTypeAnnotations".equals(attrName)) {
+ tanns = u + 8;
} else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) {
dann = u + 8;
} else if ("Synthetic".equals(attrName)) {
- access |= Opcodes.ACC_SYNTHETIC
+ context.access |= Opcodes.ACC_SYNTHETIC
| ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
} else if (ANNOTATIONS
&& "RuntimeInvisibleAnnotations".equals(attrName)) {
ianns = u + 8;
+ } else if (ANNOTATIONS
+ && "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
+ itanns = u + 8;
} else if (ANNOTATIONS
&& "RuntimeVisibleParameterAnnotations".equals(attrName)) {
mpanns = u + 8;
} else if (ANNOTATIONS
&& "RuntimeInvisibleParameterAnnotations".equals(attrName)) {
impanns = u + 8;
+ } else if ("MethodParameters".equals(attrName)) {
+ methodParameters = u + 8;
} else {
Attribute attr = readAttribute(context.attrs, attrName, u + 8,
readInt(u + 4), c, -1, null);
@@ -849,8 +923,8 @@ private int readMethod(final ClassVisitor classVisitor,
u += 2;
// visits the method declaration
- MethodVisitor mv = classVisitor.visitMethod(access, name, desc,
- signature, exceptions);
+ MethodVisitor mv = classVisitor.visitMethod(context.access,
+ context.name, context.desc, signature, exceptions);
if (mv == null) {
return u;
}
@@ -894,6 +968,13 @@ private int readMethod(final ClassVisitor classVisitor,
}
}
+ // visit the method parameters
+ if (methodParameters != 0) {
+ for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) {
+ mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2));
+ }
+ }
+
// visits the method annotations
if (ANNOTATIONS && dann != 0) {
AnnotationVisitor dv = mv.visitAnnotationDefault();
@@ -914,11 +995,27 @@ private int readMethod(final ClassVisitor classVisitor,
mv.visitAnnotation(readUTF8(v, c), false));
}
}
+ if (ANNOTATIONS && tanns != 0) {
+ for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
+ v = readAnnotationTarget(context, v);
+ v = readAnnotationValues(v + 2, c, true,
+ mv.visitTypeAnnotation(context.typeRef,
+ context.typePath, readUTF8(v, c), true));
+ }
+ }
+ if (ANNOTATIONS && itanns != 0) {
+ for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
+ v = readAnnotationTarget(context, v);
+ v = readAnnotationValues(v + 2, c, true,
+ mv.visitTypeAnnotation(context.typeRef,
+ context.typePath, readUTF8(v, c), false));
+ }
+ }
if (ANNOTATIONS && mpanns != 0) {
- readParameterAnnotations(mpanns, desc, c, true, mv);
+ readParameterAnnotations(mv, context, mpanns, true);
}
if (ANNOTATIONS && impanns != 0) {
- readParameterAnnotations(impanns, desc, c, false, mv);
+ readParameterAnnotations(mv, context, impanns, false);
}
// visits the method attributes
@@ -931,9 +1028,6 @@ private int readMethod(final ClassVisitor classVisitor,
// visits the method code
if (code != 0) {
- context.access = access;
- context.name = name;
- context.desc = desc;
mv.visitCode();
readCode(mv, context, code);
}
@@ -966,7 +1060,7 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
// reads the bytecode to find the labels
int codeStart = u;
int codeEnd = u + codeLength;
- Label[] labels = new Label[codeLength + 2];
+ Label[] labels = context.labels = new Label[codeLength + 2];
readLabel(codeLength + 1, labels);
while (u < codeEnd) {
int offset = u - codeStart;
@@ -980,6 +1074,10 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
readLabel(offset + readShort(u + 1), labels);
u += 3;
break;
+ case ClassWriter.ASM_LABEL_INSN:
+ readLabel(offset + readUnsignedShort(u + 1), labels);
+ u += 3;
+ break;
case ClassWriter.LABELW_INSN:
readLabel(offset + readInt(u + 1), labels);
u += 5;
@@ -1049,6 +1147,12 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
u += 2;
// reads the code attributes
+ int[] tanns = null; // start index of each visible type annotation
+ int[] itanns = null; // start index of each invisible type annotation
+ int tann = 0; // current index in tanns array
+ int itann = 0; // current index in itanns array
+ int ntoff = -1; // next visible type annotation code offset
+ int nitoff = -1; // next invisible type annotation code offset
int varTable = 0;
int varTypeTable = 0;
boolean zip = true;
@@ -1085,10 +1189,27 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
if (labels[label] == null) {
readLabel(label, labels).status |= Label.DEBUG;
}
- labels[label].line = readUnsignedShort(v + 12);
+ Label l = labels[label];
+ while (l.line > 0) {
+ if (l.next == null) {
+ l.next = new Label();
+ }
+ l = l.next;
+ }
+ l.line = readUnsignedShort(v + 12);
v += 4;
}
}
+ } else if (ANNOTATIONS
+ && "RuntimeVisibleTypeAnnotations".equals(attrName)) {
+ tanns = readTypeAnnotations(mv, context, u + 8, true);
+ ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1
+ : readUnsignedShort(tanns[0] + 1);
+ } else if (ANNOTATIONS
+ && "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
+ itanns = readTypeAnnotations(mv, context, u + 8, false);
+ nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1
+ : readUnsignedShort(itanns[0] + 1);
} else if (FRAMES && "StackMapTable".equals(attrName)) {
if ((context.flags & SKIP_FRAMES) == 0) {
stackMap = u + 10;
@@ -1181,8 +1302,23 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
}
}
}
+ if ((context.flags & EXPAND_ASM_INSNS) != 0) {
+ // Expanding the ASM pseudo instructions can introduce F_INSERT
+ // frames, even if the method does not currently have any frame.
+ // Also these inserted frames must be computed by simulating the
+ // effect of the bytecode instructions one by one, starting from the
+ // first one and the last existing frame (or the implicit first
+ // one). Finally, due to the way MethodWriter computes this (with
+ // the compute = INSERTED_FRAMES option), MethodWriter needs to know
+ // maxLocals before the first instruction is visited. For all these
+ // reasons we always visit the implicit first frame in this case
+ // (passing only maxLocals - the rest can be and is computed in
+ // MethodWriter).
+ mv.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
+ }
// visits the instructions
+ int opcodeDelta = (context.flags & EXPAND_ASM_INSNS) == 0 ? -33 : 0;
u = codeStart;
while (u < codeEnd) {
int offset = u - codeStart;
@@ -1190,9 +1326,15 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
// visits the label and line number for this offset, if any
Label l = labels[offset];
if (l != null) {
+ Label next = l.next;
+ l.next = null;
mv.visitLabel(l);
if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) {
mv.visitLineNumber(l.line, l);
+ while (next != null) {
+ mv.visitLineNumber(next.line, l);
+ next = next.next;
+ }
}
}
@@ -1211,7 +1353,7 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
}
}
if (frameCount > 0) {
- stackMap = readFrame(stackMap, zip, unzip, labels, frame);
+ stackMap = readFrame(stackMap, zip, unzip, frame);
--frameCount;
} else {
frame = null;
@@ -1241,9 +1383,42 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
u += 3;
break;
case ClassWriter.LABELW_INSN:
- mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]);
+ mv.visitJumpInsn(opcode + opcodeDelta, labels[offset
+ + readInt(u + 1)]);
u += 5;
break;
+ case ClassWriter.ASM_LABEL_INSN: {
+ // changes temporary opcodes 202 to 217 (inclusive), 218
+ // and 219 to IFEQ ... JSR (inclusive), IFNULL and
+ // IFNONNULL
+ opcode = opcode < 218 ? opcode - 49 : opcode - 20;
+ Label target = labels[offset + readUnsignedShort(u + 1)];
+ // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
+ // with IFNOTxxx GOTO_W , where IFNOTxxx is
+ // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
+ // and where designates the instruction just after
+ // the GOTO_W.
+ if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
+ mv.visitJumpInsn(opcode + 33, target);
+ } else {
+ opcode = opcode <= 166 ? ((opcode + 1) ^ 1) - 1
+ : opcode ^ 1;
+ Label endif = new Label();
+ mv.visitJumpInsn(opcode, endif);
+ mv.visitJumpInsn(200, target); // GOTO_W
+ mv.visitLabel(endif);
+ // since we introduced an unconditional jump instruction we
+ // also need to insert a stack map frame here, unless there
+ // is already one. The actual frame content will be computed
+ // in MethodWriter.
+ if (FRAMES && stackMap != 0
+ && (frame == null || frame.offset != offset + 3)) {
+ mv.visitFrame(ClassWriter.F_INSERT, 0, null, 0, null);
+ }
+ }
+ u += 3;
+ break;
+ }
case ClassWriter.WIDE_INSN:
opcode = b[u + 1] & 0xFF;
if (opcode == Opcodes.IINC) {
@@ -1310,6 +1485,7 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
case ClassWriter.FIELDORMETH_INSN:
case ClassWriter.ITFMETH_INSN: {
int cpIndex = items[readUnsignedShort(u + 1)];
+ boolean itf = b[cpIndex - 1] == ClassWriter.IMETH;
String iowner = readClass(cpIndex, c);
cpIndex = items[readUnsignedShort(cpIndex + 2)];
String iname = readUTF8(cpIndex, c);
@@ -1317,7 +1493,7 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
if (opcode < Opcodes.INVOKEVIRTUAL) {
mv.visitFieldInsn(opcode, iowner, iname, idesc);
} else {
- mv.visitMethodInsn(opcode, iowner, iname, idesc);
+ mv.visitMethodInsn(opcode, iowner, iname, idesc, itf);
}
if (opcode == Opcodes.INVOKEINTERFACE) {
u += 5;
@@ -1358,6 +1534,29 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
u += 4;
break;
}
+
+ // visit the instruction annotations, if any
+ while (tanns != null && tann < tanns.length && ntoff <= offset) {
+ if (ntoff == offset) {
+ int v = readAnnotationTarget(context, tanns[tann]);
+ readAnnotationValues(v + 2, c, true,
+ mv.visitInsnAnnotation(context.typeRef,
+ context.typePath, readUTF8(v, c), true));
+ }
+ ntoff = ++tann >= tanns.length || readByte(tanns[tann]) < 0x43 ? -1
+ : readUnsignedShort(tanns[tann] + 1);
+ }
+ while (itanns != null && itann < itanns.length && nitoff <= offset) {
+ if (nitoff == offset) {
+ int v = readAnnotationTarget(context, itanns[itann]);
+ readAnnotationValues(v + 2, c, true,
+ mv.visitInsnAnnotation(context.typeRef,
+ context.typePath, readUTF8(v, c), false));
+ }
+ nitoff = ++itann >= itanns.length
+ || readByte(itanns[itann]) < 0x43 ? -1
+ : readUnsignedShort(itanns[itann] + 1);
+ }
}
if (labels[codeLength] != null) {
mv.visitLabel(labels[codeLength]);
@@ -1397,6 +1596,32 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
}
}
+ // visits the local variables type annotations
+ if (tanns != null) {
+ for (int i = 0; i < tanns.length; ++i) {
+ if ((readByte(tanns[i]) >> 1) == (0x40 >> 1)) {
+ int v = readAnnotationTarget(context, tanns[i]);
+ v = readAnnotationValues(v + 2, c, true,
+ mv.visitLocalVariableAnnotation(context.typeRef,
+ context.typePath, context.start,
+ context.end, context.index, readUTF8(v, c),
+ true));
+ }
+ }
+ }
+ if (itanns != null) {
+ for (int i = 0; i < itanns.length; ++i) {
+ if ((readByte(itanns[i]) >> 1) == (0x40 >> 1)) {
+ int v = readAnnotationTarget(context, itanns[i]);
+ v = readAnnotationValues(v + 2, c, true,
+ mv.visitLocalVariableAnnotation(context.typeRef,
+ context.typePath, context.start,
+ context.end, context.index, readUTF8(v, c),
+ false));
+ }
+ }
+ }
+
// visits the code attributes
while (attributes != null) {
Attribute attr = attributes.next;
@@ -1409,25 +1634,176 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
mv.visitMaxs(maxStack, maxLocals);
}
+ /**
+ * Parses a type annotation table to find the labels, and to visit the try
+ * catch block annotations.
+ *
+ * @param u
+ * the start offset of a type annotation table.
+ * @param mv
+ * the method visitor to be used to visit the try catch block
+ * annotations.
+ * @param context
+ * information about the class being parsed.
+ * @param visible
+ * if the type annotation table to parse contains runtime visible
+ * annotations.
+ * @return the start offset of each type annotation in the parsed table.
+ */
+ private int[] readTypeAnnotations(final MethodVisitor mv,
+ final Context context, int u, boolean visible) {
+ char[] c = context.buffer;
+ int[] offsets = new int[readUnsignedShort(u)];
+ u += 2;
+ for (int i = 0; i < offsets.length; ++i) {
+ offsets[i] = u;
+ int target = readInt(u);
+ switch (target >>> 24) {
+ case 0x00: // CLASS_TYPE_PARAMETER
+ case 0x01: // METHOD_TYPE_PARAMETER
+ case 0x16: // METHOD_FORMAL_PARAMETER
+ u += 2;
+ break;
+ case 0x13: // FIELD
+ case 0x14: // METHOD_RETURN
+ case 0x15: // METHOD_RECEIVER
+ u += 1;
+ break;
+ case 0x40: // LOCAL_VARIABLE
+ case 0x41: // RESOURCE_VARIABLE
+ for (int j = readUnsignedShort(u + 1); j > 0; --j) {
+ int start = readUnsignedShort(u + 3);
+ int length = readUnsignedShort(u + 5);
+ readLabel(start, context.labels);
+ readLabel(start + length, context.labels);
+ u += 6;
+ }
+ u += 3;
+ break;
+ case 0x47: // CAST
+ case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
+ case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT
+ case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
+ case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT
+ u += 4;
+ break;
+ // case 0x10: // CLASS_EXTENDS
+ // case 0x11: // CLASS_TYPE_PARAMETER_BOUND
+ // case 0x12: // METHOD_TYPE_PARAMETER_BOUND
+ // case 0x17: // THROWS
+ // case 0x42: // EXCEPTION_PARAMETER
+ // case 0x43: // INSTANCEOF
+ // case 0x44: // NEW
+ // case 0x45: // CONSTRUCTOR_REFERENCE
+ // case 0x46: // METHOD_REFERENCE
+ default:
+ u += 3;
+ break;
+ }
+ int pathLength = readByte(u);
+ if ((target >>> 24) == 0x42) {
+ TypePath path = pathLength == 0 ? null : new TypePath(b, u);
+ u += 1 + 2 * pathLength;
+ u = readAnnotationValues(u + 2, c, true,
+ mv.visitTryCatchAnnotation(target, path,
+ readUTF8(u, c), visible));
+ } else {
+ u = readAnnotationValues(u + 3 + 2 * pathLength, c, true, null);
+ }
+ }
+ return offsets;
+ }
+
+ /**
+ * Parses the header of a type annotation to extract its target_type and
+ * target_path (the result is stored in the given context), and returns the
+ * start offset of the rest of the type_annotation structure (i.e. the
+ * offset to the type_index field, which is followed by
+ * num_element_value_pairs and then the name,value pairs).
+ *
+ * @param context
+ * information about the class being parsed. This is where the
+ * extracted target_type and target_path must be stored.
+ * @param u
+ * the start offset of a type_annotation structure.
+ * @return the start offset of the rest of the type_annotation structure.
+ */
+ private int readAnnotationTarget(final Context context, int u) {
+ int target = readInt(u);
+ switch (target >>> 24) {
+ case 0x00: // CLASS_TYPE_PARAMETER
+ case 0x01: // METHOD_TYPE_PARAMETER
+ case 0x16: // METHOD_FORMAL_PARAMETER
+ target &= 0xFFFF0000;
+ u += 2;
+ break;
+ case 0x13: // FIELD
+ case 0x14: // METHOD_RETURN
+ case 0x15: // METHOD_RECEIVER
+ target &= 0xFF000000;
+ u += 1;
+ break;
+ case 0x40: // LOCAL_VARIABLE
+ case 0x41: { // RESOURCE_VARIABLE
+ target &= 0xFF000000;
+ int n = readUnsignedShort(u + 1);
+ context.start = new Label[n];
+ context.end = new Label[n];
+ context.index = new int[n];
+ u += 3;
+ for (int i = 0; i < n; ++i) {
+ int start = readUnsignedShort(u);
+ int length = readUnsignedShort(u + 2);
+ context.start[i] = readLabel(start, context.labels);
+ context.end[i] = readLabel(start + length, context.labels);
+ context.index[i] = readUnsignedShort(u + 4);
+ u += 6;
+ }
+ break;
+ }
+ case 0x47: // CAST
+ case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
+ case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT
+ case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
+ case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT
+ target &= 0xFF0000FF;
+ u += 4;
+ break;
+ // case 0x10: // CLASS_EXTENDS
+ // case 0x11: // CLASS_TYPE_PARAMETER_BOUND
+ // case 0x12: // METHOD_TYPE_PARAMETER_BOUND
+ // case 0x17: // THROWS
+ // case 0x42: // EXCEPTION_PARAMETER
+ // case 0x43: // INSTANCEOF
+ // case 0x44: // NEW
+ // case 0x45: // CONSTRUCTOR_REFERENCE
+ // case 0x46: // METHOD_REFERENCE
+ default:
+ target &= (target >>> 24) < 0x43 ? 0xFFFFFF00 : 0xFF000000;
+ u += 3;
+ break;
+ }
+ int pathLength = readByte(u);
+ context.typeRef = target;
+ context.typePath = pathLength == 0 ? null : new TypePath(b, u);
+ return u + 1 + 2 * pathLength;
+ }
+
/**
* Reads parameter annotations and makes the given visitor visit them.
*
+ * @param mv
+ * the visitor that must visit the annotations.
+ * @param context
+ * information about the class being parsed.
* @param v
* start offset in {@link #b b} of the annotations to be read.
- * @param desc
- * the method descriptor.
- * @param buf
- * buffer to be used to call {@link #readUTF8 readUTF8},
- * {@link #readClass(int,char[]) readClass} or {@link #readConst
- * readConst}.
* @param visible
* true if the annotations to be read are visible at
* runtime.
- * @param mv
- * the visitor that must visit the annotations.
*/
- private void readParameterAnnotations(int v, final String desc,
- final char[] buf, final boolean visible, final MethodVisitor mv) {
+ private void readParameterAnnotations(final MethodVisitor mv,
+ final Context context, int v, final boolean visible) {
int i;
int n = b[v++] & 0xFF;
// workaround for a bug in javac (javac compiler generates a parameter
@@ -1436,7 +1812,7 @@ private void readParameterAnnotations(int v, final String desc,
// equal to the number of parameters in the method descriptor - which
// includes the synthetic parameters added by the compiler). This work-
// around supposes that the synthetic parameters are the first ones.
- int synthetics = Type.getArgumentTypes(desc).length - n;
+ int synthetics = Type.getArgumentTypes(context.desc).length - n;
AnnotationVisitor av;
for (i = 0; i < synthetics; ++i) {
// virtual annotation to detect synthetic parameters in MethodWriter
@@ -1445,12 +1821,13 @@ private void readParameterAnnotations(int v, final String desc,
av.visitEnd();
}
}
+ char[] c = context.buffer;
for (; i < n + synthetics; ++i) {
int j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
- av = mv.visitParameterAnnotation(i, readUTF8(v, buf), visible);
- v = readAnnotationValues(v + 2, buf, true, av);
+ av = mv.visitParameterAnnotation(i, readUTF8(v, c), visible);
+ v = readAnnotationValues(v + 2, c, true, av);
}
}
}
@@ -1531,8 +1908,7 @@ private int readAnnotationValue(int v, final char[] buf, final String name,
v += 2;
break;
case 'B': // pointer to CONSTANT_Byte
- av.visit(name,
- new Byte((byte) readInt(items[readUnsignedShort(v)])));
+ av.visit(name, (byte) readInt(items[readUnsignedShort(v)]));
v += 2;
break;
case 'Z': // pointer to CONSTANT_Boolean
@@ -1542,13 +1918,11 @@ private int readAnnotationValue(int v, final char[] buf, final String name,
v += 2;
break;
case 'S': // pointer to CONSTANT_Short
- av.visit(name, new Short(
- (short) readInt(items[readUnsignedShort(v)])));
+ av.visit(name, (short) readInt(items[readUnsignedShort(v)]));
v += 2;
break;
case 'C': // pointer to CONSTANT_Char
- av.visit(name, new Character(
- (char) readInt(items[readUnsignedShort(v)])));
+ av.visit(name, (char) readInt(items[readUnsignedShort(v)]));
v += 2;
break;
case 's': // pointer to CONSTANT_Utf8
@@ -1729,17 +2103,14 @@ private void getImplicitFrame(final Context frame) {
* if the stack map frame at stackMap is compressed or not.
* @param unzip
* if the stack map frame must be uncompressed.
- * @param labels
- * the labels of the method currently being parsed, indexed by
- * their offset. A new label for the parsed stack map frame is
- * stored in this array if it does not already exist.
* @param frame
* where the parsed stack map frame must be stored.
* @return the offset of the first byte following the parsed frame.
*/
private int readFrame(int stackMap, boolean zip, boolean unzip,
- Label[] labels, Context frame) {
+ Context frame) {
char[] c = frame.buffer;
+ Label[] labels = frame.labels;
int tag;
int delta;
if (zip) {
@@ -2175,13 +2546,13 @@ public Object readConst(final int item, final char[] buf) {
int index = items[item];
switch (b[index - 1]) {
case ClassWriter.INT:
- return new Integer(readInt(index));
+ return readInt(index);
case ClassWriter.FLOAT:
- return new Float(Float.intBitsToFloat(readInt(index)));
+ return Float.intBitsToFloat(readInt(index));
case ClassWriter.LONG:
- return new Long(readLong(index));
+ return readLong(index);
case ClassWriter.DOUBLE:
- return new Double(Double.longBitsToDouble(readLong(index)));
+ return Double.longBitsToDouble(readLong(index));
case ClassWriter.CLASS:
return Type.getObjectType(readUTF8(index, buf));
case ClassWriter.STR:
@@ -2192,11 +2563,12 @@ public Object readConst(final int item, final char[] buf) {
int tag = readByte(index);
int[] items = this.items;
int cpIndex = items[readUnsignedShort(index + 1)];
+ boolean itf = b[cpIndex - 1] == ClassWriter.IMETH;
String owner = readClass(cpIndex, buf);
cpIndex = items[readUnsignedShort(cpIndex + 2)];
String name = readUTF8(cpIndex, buf);
String desc = readUTF8(cpIndex + 2, buf);
- return new Handle(tag, owner, name, desc);
+ return new Handle(tag, owner, name, desc, itf);
}
}
}
diff --git a/src/java/nginx/clojure/asm/ClassVisitor.java b/src/java/nginx/clojure/asm/ClassVisitor.java
index 0c294864..85a60550 100644
--- a/src/java/nginx/clojure/asm/ClassVisitor.java
+++ b/src/java/nginx/clojure/asm/ClassVisitor.java
@@ -33,8 +33,9 @@
* A visitor to visit a Java class. The methods of this class must be called in
* the following order: visit [ visitSource ] [
* visitOuterClass ] ( visitAnnotation |
- * visitAttribute )* ( visitInnerClass | visitField |
- * visitMethod )* visitEnd.
+ * visitTypeAnnotation | visitAttribute )* (
+ * visitInnerClass | visitField | visitMethod )*
+ * visitEnd.
*
* @author Eric Bruneton
*/
@@ -42,7 +43,7 @@ public abstract class ClassVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
- * must be one of {@link Opcodes#ASM4}.
+ * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
*/
protected final int api;
@@ -57,7 +58,7 @@ public abstract class ClassVisitor {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
*/
public ClassVisitor(final int api) {
this(api, null);
@@ -68,13 +69,13 @@ public ClassVisitor(final int api) {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param cv
* the class visitor to which this visitor must delegate method
* calls. May be null.
*/
public ClassVisitor(final int api, final ClassVisitor cv) {
- if (api != Opcodes.ASM4) {
+ if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
throw new IllegalArgumentException();
}
this.api = api;
@@ -168,6 +169,39 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
+ /**
+ * Visits an annotation on a type in the class signature.
+ *
+ * @param typeRef
+ * a reference to the annotated type. The sort of this type
+ * reference must be {@link TypeReference#CLASS_TYPE_PARAMETER
+ * CLASS_TYPE_PARAMETER},
+ * {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND
+ * CLASS_TYPE_PARAMETER_BOUND} or
+ * {@link TypeReference#CLASS_EXTENDS CLASS_EXTENDS}. See
+ * {@link TypeReference}.
+ * @param typePath
+ * the path to the annotated type argument, wildcard bound, array
+ * element type, or static inner type within 'typeRef'. May be
+ * null if the annotation targets 'typeRef' as a whole.
+ * @param desc
+ * the class descriptor of the annotation class.
+ * @param visible
+ * true if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or null if
+ * this visitor is not interested in visiting this annotation.
+ */
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ if (api < Opcodes.ASM5) {
+ throw new RuntimeException();
+ }
+ if (cv != null) {
+ return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
+ }
+ return null;
+ }
+
/**
* Visits a non standard attribute of the class.
*
diff --git a/src/java/nginx/clojure/asm/ClassWriter.java b/src/java/nginx/clojure/asm/ClassWriter.java
index b921e0ee..3e57d330 100644
--- a/src/java/nginx/clojure/asm/ClassWriter.java
+++ b/src/java/nginx/clojure/asm/ClassWriter.java
@@ -58,8 +58,8 @@ public class ClassWriter extends ClassVisitor {
* {@link MethodVisitor#visitFrame} method are ignored, and the stack map
* frames are recomputed from the methods bytecode. The arguments of the
* {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and
- * recomputed from the bytecode. In other words, computeFrames implies
- * computeMaxs.
+ * recomputed from the bytecode. In other words, COMPUTE_FRAMES implies
+ * COMPUTE_MAXS.
*
* @see #ClassWriter(int)
*/
@@ -167,6 +167,22 @@ public class ClassWriter extends ClassVisitor {
*/
static final int WIDE_INSN = 17;
+ /**
+ * The type of the ASM pseudo instructions with an unsigned 2 bytes offset
+ * label (see Label#resolve).
+ */
+ static final int ASM_LABEL_INSN = 18;
+
+ /**
+ * Represents a frame inserted between already existing frames. This kind of
+ * frame can only be used if the frame content can be computed from the
+ * previous existing frame and from the instructions between this existing
+ * frame and the inserted one, without any knowledge of the type hierarchy.
+ * This kind of frame is only used when an unconditional jump is inserted in
+ * a method while expanding an ASM pseudo instruction (see ClassReader).
+ */
+ static final int F_INSERT = 256;
+
/**
* The instruction types of all JVM opcodes.
*/
@@ -416,6 +432,16 @@ public class ClassWriter extends ClassVisitor {
*/
private AnnotationWriter ianns;
+ /**
+ * The runtime visible type annotations of this class.
+ */
+ private AnnotationWriter tanns;
+
+ /**
+ * The runtime invisible type annotations of this class.
+ */
+ private AnnotationWriter itanns;
+
/**
* The non standard attributes of this class.
*/
@@ -474,25 +500,19 @@ public class ClassWriter extends ClassVisitor {
MethodWriter lastMethod;
/**
- * true if the maximum stack size and number of local variables
- * must be automatically computed.
- */
- private boolean computeMaxs;
-
- /**
- * true if the stack map frames must be recomputed from scratch.
+ * Indicates what must be automatically computed.
+ *
+ * @see MethodWriter#compute
*/
- private boolean computeFrames;
+ private int compute;
/**
- * true if the stack map tables of this class are invalid. The
- * {@link MethodWriter#resizeInstructions} method cannot transform existing
- * stack map tables, and so produces potentially invalid classes when it is
- * executed. In this case the class is reread and rewritten with the
- * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize
- * stack map tables when this option is used).
+ * true if some methods have wide forward jumps using ASM pseudo
+ * instructions, which need to be expanded into sequences of standard
+ * bytecode instructions. In this case the class is re-read and re-written
+ * with a ClassReader -> ClassWriter chain to perform this transformation.
*/
- boolean invalidFrames;
+ boolean hasAsmInsns;
// ------------------------------------------------------------------------
// Static initializer
@@ -507,7 +527,7 @@ public class ClassWriter extends ClassVisitor {
String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
+ "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA"
- + "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ";
+ + "AAAAGGGGGGGHIFBFAAFFAARQJJKKSSSSSSSSSSSSSSSSSS";
for (i = 0; i < b.length; ++i) {
b[i] = (byte) (s.charAt(i) - 'A');
}
@@ -561,7 +581,7 @@ public class ClassWriter extends ClassVisitor {
// // temporary opcodes used internally by ASM - see Label and
// MethodWriter
// for (i = 202; i < 220; ++i) {
- // b[i] = LABEL_INSN;
+ // b[i] = ASM_LABEL_INSN;
// }
//
// // LDC(_W) instructions
@@ -595,7 +615,7 @@ public class ClassWriter extends ClassVisitor {
* {@link #COMPUTE_FRAMES}.
*/
public ClassWriter(final int flags) {
- super(Opcodes.ASM4);
+ super(Opcodes.ASM5);
index = 1;
pool = new ByteVector();
items = new Item[256];
@@ -604,8 +624,9 @@ public ClassWriter(final int flags) {
key2 = new Item();
key3 = new Item();
key4 = new Item();
- this.computeMaxs = (flags & COMPUTE_MAXS) != 0;
- this.computeFrames = (flags & COMPUTE_FRAMES) != 0;
+ this.compute = (flags & COMPUTE_FRAMES) != 0 ? MethodWriter.FRAMES
+ : ((flags & COMPUTE_MAXS) != 0 ? MethodWriter.MAXS
+ : MethodWriter.NOTHING);
}
/**
@@ -635,9 +656,9 @@ public ClassWriter(final int flags) {
* @param flags
* option flags that can be used to modify the default behavior
* of this class. These option flags do not affect methods
- * that are copied as is in the new class. This means that the
- * maximum stack size nor the stack frames will be computed for
- * these methods. See {@link #COMPUTE_MAXS},
+ * that are copied as is in the new class. This means that
+ * neither the maximum stack size nor the stack frames will be
+ * computed for these methods. See {@link #COMPUTE_MAXS},
* {@link #COMPUTE_FRAMES}.
*/
public ClassWriter(final ClassReader classReader, final int flags) {
@@ -677,7 +698,8 @@ public final void visitSource(final String file, final String debug) {
sourceFile = newUTF8(file);
}
if (debug != null) {
- sourceDebug = new ByteVector().putUTF8(debug);
+ sourceDebug = new ByteVector().encodeUTF8(debug, 0,
+ Integer.MAX_VALUE);
}
}
@@ -710,6 +732,29 @@ public final AnnotationVisitor visitAnnotation(final String desc,
return aw;
}
+ @Override
+ public final AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, final String desc, final boolean visible) {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ // write target_type and target_info
+ AnnotationWriter.putTarget(typeRef, typePath, bv);
+ // write type, and reserve space for values count
+ bv.putShort(newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv,
+ bv.length - 2);
+ if (visible) {
+ aw.next = tanns;
+ tanns = aw;
+ } else {
+ aw.next = itanns;
+ itanns = aw;
+ }
+ return aw;
+ }
+
@Override
public final void visitAttribute(final Attribute attr) {
attr.next = attrs;
@@ -722,11 +767,29 @@ public final void visitInnerClass(final String name,
if (innerClasses == null) {
innerClasses = new ByteVector();
}
- ++innerClassesCount;
- innerClasses.putShort(name == null ? 0 : newClass(name));
- innerClasses.putShort(outerName == null ? 0 : newClass(outerName));
- innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName));
- innerClasses.putShort(access);
+ // Sec. 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the
+ // constant_pool table which represents a class or interface C that is
+ // not a package member must have exactly one corresponding entry in the
+ // classes array". To avoid duplicates we keep track in the intVal field
+ // of the Item of each CONSTANT_Class_info entry C whether an inner
+ // class entry has already been added for C (this field is unused for
+ // class entries, and changing its value does not change the hashcode
+ // and equality tests). If so we store the index of this inner class
+ // entry (plus one) in intVal. This hack allows duplicate detection in
+ // O(1) time.
+ Item nameItem = newClassItem(name);
+ if (nameItem.intVal == 0) {
+ ++innerClassesCount;
+ innerClasses.putShort(nameItem.index);
+ innerClasses.putShort(outerName == null ? 0 : newClass(outerName));
+ innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName));
+ innerClasses.putShort(access);
+ nameItem.intVal = innerClassesCount;
+ } else {
+ // Compare the inner classes entry nameItem.intVal - 1 with the
+ // arguments of this method and throw an exception if there is a
+ // difference?
+ }
}
@Override
@@ -739,7 +802,7 @@ public final FieldVisitor visitField(final int access, final String name,
public final MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
return new MethodWriter(this, access, name, desc, signature,
- exceptions, computeMaxs, computeFrames);
+ exceptions, compute);
}
@Override
@@ -795,7 +858,7 @@ public byte[] toByteArray() {
}
if (sourceDebug != null) {
++attributeCount;
- size += sourceDebug.length + 4;
+ size += sourceDebug.length + 6;
newUTF8("SourceDebugExtension");
}
if (enclosingMethodOwner != 0) {
@@ -831,6 +894,16 @@ public byte[] toByteArray() {
size += 8 + ianns.getSize();
newUTF8("RuntimeInvisibleAnnotations");
}
+ if (ClassReader.ANNOTATIONS && tanns != null) {
+ ++attributeCount;
+ size += 8 + tanns.getSize();
+ newUTF8("RuntimeVisibleTypeAnnotations");
+ }
+ if (ClassReader.ANNOTATIONS && itanns != null) {
+ ++attributeCount;
+ size += 8 + itanns.getSize();
+ newUTF8("RuntimeInvisibleTypeAnnotations");
+ }
if (attrs != null) {
attributeCount += attrs.getCount();
size += attrs.getSize(this, null, 0, -1, -1);
@@ -874,9 +947,9 @@ public byte[] toByteArray() {
out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile);
}
if (sourceDebug != null) {
- int len = sourceDebug.length - 2;
+ int len = sourceDebug.length;
out.putShort(newUTF8("SourceDebugExtension")).putInt(len);
- out.putByteArray(sourceDebug.data, 2, len);
+ out.putByteArray(sourceDebug.data, 0, len);
}
if (enclosingMethodOwner != 0) {
out.putShort(newUTF8("EnclosingMethod")).putInt(4);
@@ -904,25 +977,31 @@ public byte[] toByteArray() {
out.putShort(newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out);
}
+ if (ClassReader.ANNOTATIONS && tanns != null) {
+ out.putShort(newUTF8("RuntimeVisibleTypeAnnotations"));
+ tanns.put(out);
+ }
+ if (ClassReader.ANNOTATIONS && itanns != null) {
+ out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations"));
+ itanns.put(out);
+ }
if (attrs != null) {
attrs.put(this, null, 0, -1, -1, out);
}
- if (invalidFrames) {
+ if (hasAsmInsns) {
anns = null;
ianns = null;
attrs = null;
innerClassesCount = 0;
innerClasses = null;
- bootstrapMethodsCount = 0;
- bootstrapMethods = null;
firstField = null;
lastField = null;
firstMethod = null;
lastMethod = null;
- computeMaxs = false;
- computeFrames = true;
- invalidFrames = false;
- new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES);
+ compute = MethodWriter.INSERTED_FRAMES;
+ hasAsmInsns = false;
+ new ClassReader(out.data).accept(this, ClassReader.EXPAND_FRAMES
+ | ClassReader.EXPAND_ASM_INSNS);
return toByteArray();
}
return out.data;
@@ -982,7 +1061,7 @@ Item newConstItem(final Object cst) {
}
} else if (cst instanceof Handle) {
Handle h = (Handle) cst;
- return newHandleItem(h.tag, h.owner, h.name, h.desc);
+ return newHandleItem(h.tag, h.owner, h.name, h.desc, h.itf);
} else {
throw new IllegalArgumentException("value " + cst);
}
@@ -1117,10 +1196,12 @@ public int newMethodType(final String methodDesc) {
* the name of the field or method.
* @param desc
* the descriptor of the field or method.
+ * @param itf
+ * true if the owner is an interface.
* @return a new or an already existing method type reference item.
*/
Item newHandleItem(final int tag, final String owner, final String name,
- final String desc) {
+ final String desc, final boolean itf) {
key4.set(HANDLE_BASE + tag, owner, name, desc);
Item result = get(key4);
if (result == null) {
@@ -1129,8 +1210,7 @@ Item newHandleItem(final int tag, final String owner, final String name,
} else {
put112(HANDLE,
tag,
- newMethod(owner, name, desc,
- tag == Opcodes.H_INVOKEINTERFACE));
+ newMethod(owner, name, desc, itf));
}
result = new Item(index++, key4);
put(result);
@@ -1160,12 +1240,46 @@ Item newHandleItem(final int tag, final String owner, final String name,
* the descriptor of the field or method.
* @return the index of a new or already existing method type reference
* item.
+ *
+ * @deprecated this method is superseded by
+ * {@link #newHandle(int, String, String, String, boolean)}.
*/
+ @Deprecated
public int newHandle(final int tag, final String owner, final String name,
final String desc) {
- return newHandleItem(tag, owner, name, desc).index;
+ return newHandle(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE);
}
+ /**
+ * Adds a handle to the constant pool of the class being build. Does nothing
+ * if the constant pool already contains a similar item. This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by
+ * class generators or adapters.
+ *
+ * @param tag
+ * the kind of this handle. Must be {@link Opcodes#H_GETFIELD},
+ * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD},
+ * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL},
+ * {@link Opcodes#H_INVOKESTATIC},
+ * {@link Opcodes#H_INVOKESPECIAL},
+ * {@link Opcodes#H_NEWINVOKESPECIAL} or
+ * {@link Opcodes#H_INVOKEINTERFACE}.
+ * @param owner
+ * the internal name of the field or method owner class.
+ * @param name
+ * the name of the field or method.
+ * @param desc
+ * the descriptor of the field or method.
+ * @param itf
+ * true if the owner is an interface.
+ * @return the index of a new or already existing method type reference
+ * item.
+ */
+ public int newHandle(final int tag, final String owner, final String name,
+ final String desc, final boolean itf) {
+ return newHandleItem(tag, owner, name, desc, itf).index;
+ }
+
/**
* Adds an invokedynamic reference to the constant pool of the class being
* build. Does nothing if the constant pool already contains a similar item.
@@ -1195,7 +1309,7 @@ Item newInvokeDynamicItem(final String name, final String desc,
int hashCode = bsm.hashCode();
bootstrapMethods.putShort(newHandle(bsm.tag, bsm.owner, bsm.name,
- bsm.desc));
+ bsm.desc, bsm.isInterface()));
int argsLength = bsmArgs.length;
bootstrapMethods.putShort(argsLength);
@@ -1590,7 +1704,7 @@ int getMergedType(final int type1, final int type2) {
/**
* Returns the common super type of the two given types. The default
- * implementation of this method loads the two given classes and uses
+ * implementation of this method loads the two given classes and uses
* the java.lang.Class methods to find the common super class. It can be
* overridden to compute this common super type in other ways, in particular
* without actually loading any class, or to take into account the class
diff --git a/src/java/nginx/clojure/asm/Context.java b/src/java/nginx/clojure/asm/Context.java
index f0e446ba..e2d3c450 100644
--- a/src/java/nginx/clojure/asm/Context.java
+++ b/src/java/nginx/clojure/asm/Context.java
@@ -72,11 +72,46 @@ class Context {
*/
String desc;
+ /**
+ * The label objects, indexed by bytecode offset, of the method currently
+ * being parsed (only bytecode offsets for which a label is needed have a
+ * non null associated Label object).
+ */
+ Label[] labels;
+
+ /**
+ * The target of the type annotation currently being parsed.
+ */
+ int typeRef;
+
+ /**
+ * The path of the type annotation currently being parsed.
+ */
+ TypePath typePath;
+
/**
* The offset of the latest stack map frame that has been parsed.
*/
int offset;
+ /**
+ * The labels corresponding to the start of the local variable ranges in the
+ * local variable type annotation currently being parsed.
+ */
+ Label[] start;
+
+ /**
+ * The labels corresponding to the end of the local variable ranges in the
+ * local variable type annotation currently being parsed.
+ */
+ Label[] end;
+
+ /**
+ * The local variable indices for each local variable range in the local
+ * variable type annotation currently being parsed.
+ */
+ int[] index;
+
/**
* The encoding of the latest stack map frame that has been parsed.
*/
diff --git a/src/java/nginx/clojure/asm/CurrentFrame.java b/src/java/nginx/clojure/asm/CurrentFrame.java
new file mode 100644
index 00000000..4fa5c6d6
--- /dev/null
+++ b/src/java/nginx/clojure/asm/CurrentFrame.java
@@ -0,0 +1,56 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package nginx.clojure.asm;
+
+/**
+ * Information about the input stack map frame at the "current" instruction of a
+ * method. This is implemented as a Frame subclass for a "basic block"
+ * containing only one instruction.
+ *
+ * @author Eric Bruneton
+ */
+class CurrentFrame extends Frame {
+
+ /**
+ * Sets this CurrentFrame to the input stack map frame of the next "current"
+ * instruction, i.e. the instruction just after the given one. It is assumed
+ * that the value of this object when this method is called is the stack map
+ * frame status just before the given instruction is executed.
+ */
+ @Override
+ void execute(int opcode, int arg, ClassWriter cw, Item item) {
+ super.execute(opcode, arg, cw, item);
+ Frame successor = new Frame();
+ merge(cw, successor, 0);
+ set(successor);
+ owner.inputStackTop = 0;
+ }
+}
diff --git a/src/java/nginx/clojure/asm/FieldVisitor.java b/src/java/nginx/clojure/asm/FieldVisitor.java
index a2d39981..f1ea3cf7 100644
--- a/src/java/nginx/clojure/asm/FieldVisitor.java
+++ b/src/java/nginx/clojure/asm/FieldVisitor.java
@@ -31,8 +31,8 @@
/**
* A visitor to visit a Java field. The methods of this class must be called in
- * the following order: ( visitAnnotation | visitAttribute )*
- * visitEnd.
+ * the following order: ( visitAnnotation |
+ * visitTypeAnnotation | visitAttribute )* visitEnd.
*
* @author Eric Bruneton
*/
@@ -40,7 +40,7 @@ public abstract class FieldVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
- * must be one of {@link Opcodes#ASM4}.
+ * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
*/
protected final int api;
@@ -55,7 +55,7 @@ public abstract class FieldVisitor {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
*/
public FieldVisitor(final int api) {
this(api, null);
@@ -66,13 +66,13 @@ public FieldVisitor(final int api) {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param fv
* the field visitor to which this visitor must delegate method
* calls. May be null.
*/
public FieldVisitor(final int api, final FieldVisitor fv) {
- if (api != Opcodes.ASM4) {
+ if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
throw new IllegalArgumentException();
}
this.api = api;
@@ -96,6 +96,35 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
+ /**
+ * Visits an annotation on the type of the field.
+ *
+ * @param typeRef
+ * a reference to the annotated type. The sort of this type
+ * reference must be {@link TypeReference#FIELD FIELD}. See
+ * {@link TypeReference}.
+ * @param typePath
+ * the path to the annotated type argument, wildcard bound, array
+ * element type, or static inner type within 'typeRef'. May be
+ * null if the annotation targets 'typeRef' as a whole.
+ * @param desc
+ * the class descriptor of the annotation class.
+ * @param visible
+ * true if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or null if
+ * this visitor is not interested in visiting this annotation.
+ */
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ if (api < Opcodes.ASM5) {
+ throw new RuntimeException();
+ }
+ if (fv != null) {
+ return fv.visitTypeAnnotation(typeRef, typePath, desc, visible);
+ }
+ return null;
+ }
+
/**
* Visits a non standard attribute of the field.
*
diff --git a/src/java/nginx/clojure/asm/FieldWriter.java b/src/java/nginx/clojure/asm/FieldWriter.java
index 6a0b706b..925b23a6 100644
--- a/src/java/nginx/clojure/asm/FieldWriter.java
+++ b/src/java/nginx/clojure/asm/FieldWriter.java
@@ -80,6 +80,17 @@ final class FieldWriter extends FieldVisitor {
*/
private AnnotationWriter ianns;
+ /**
+ * The runtime visible type annotations of this field. May be null.
+ */
+ private AnnotationWriter tanns;
+
+ /**
+ * The runtime invisible type annotations of this field. May be
+ * null.
+ */
+ private AnnotationWriter itanns;
+
/**
* The non standard attributes of this field. May be null.
*/
@@ -107,7 +118,7 @@ final class FieldWriter extends FieldVisitor {
*/
FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) {
- super(Opcodes.ASM4);
+ super(Opcodes.ASM5);
if (cw.firstField == null) {
cw.firstField = this;
} else {
@@ -150,6 +161,29 @@ public AnnotationVisitor visitAnnotation(final String desc,
return aw;
}
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(final int typeRef,
+ final TypePath typePath, final String desc, final boolean visible) {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ // write target_type and target_info
+ AnnotationWriter.putTarget(typeRef, typePath, bv);
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
+ bv.length - 2);
+ if (visible) {
+ aw.next = tanns;
+ tanns = aw;
+ } else {
+ aw.next = itanns;
+ itanns = aw;
+ }
+ return aw;
+ }
+
@Override
public void visitAttribute(final Attribute attr) {
attr.next = attrs;
@@ -198,6 +232,14 @@ int getSize() {
cw.newUTF8("RuntimeInvisibleAnnotations");
size += 8 + ianns.getSize();
}
+ if (ClassReader.ANNOTATIONS && tanns != null) {
+ cw.newUTF8("RuntimeVisibleTypeAnnotations");
+ size += 8 + tanns.getSize();
+ }
+ if (ClassReader.ANNOTATIONS && itanns != null) {
+ cw.newUTF8("RuntimeInvisibleTypeAnnotations");
+ size += 8 + itanns.getSize();
+ }
if (attrs != null) {
size += attrs.getSize(cw, null, 0, -1, -1);
}
@@ -237,6 +279,12 @@ void put(final ByteVector out) {
if (ClassReader.ANNOTATIONS && ianns != null) {
++attributeCount;
}
+ if (ClassReader.ANNOTATIONS && tanns != null) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && itanns != null) {
+ ++attributeCount;
+ }
if (attrs != null) {
attributeCount += attrs.getCount();
}
@@ -266,6 +314,14 @@ void put(final ByteVector out) {
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out);
}
+ if (ClassReader.ANNOTATIONS && tanns != null) {
+ out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
+ tanns.put(out);
+ }
+ if (ClassReader.ANNOTATIONS && itanns != null) {
+ out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
+ itanns.put(out);
+ }
if (attrs != null) {
attrs.put(cw, null, 0, -1, -1, out);
}
diff --git a/src/java/nginx/clojure/asm/Frame.java b/src/java/nginx/clojure/asm/Frame.java
index 1d5378ee..6df82b84 100644
--- a/src/java/nginx/clojure/asm/Frame.java
+++ b/src/java/nginx/clojure/asm/Frame.java
@@ -34,7 +34,7 @@
*
* @author Eric Bruneton
*/
-final class Frame {
+class Frame {
/*
* Frames are computed in a two steps process: during the visit of each
@@ -70,8 +70,8 @@ final class Frame {
* stack types. VALUE depends on KIND. For LOCAL types, it is an index in
* the input local variable types. For STACK types, it is a position
* relatively to the top of input frame stack. For BASE types, it is either
- * one of the constants defined in FrameVisitor, or for OBJECT and
- * UNINITIALIZED types, a tag and an index in the type table.
+ * one of the constants defined below, or for OBJECT and UNINITIALIZED
+ * types, a tag and an index in the type table.
*
* Output frames can contain types of any kind and with a positive or
* negative dimension (and even unassigned types, represented by 0 - which
@@ -496,7 +496,7 @@ final class Frame {
* When the stack map frames are completely computed, this field is the
* actual number of types in {@link #outputStack}.
*/
- private int outputStackTop;
+ int outputStackTop;
/**
* Number of types that are initialized in the basic block.
@@ -520,6 +520,110 @@ final class Frame {
*/
private int[] initializations;
+ /**
+ * Sets this frame to the given value.
+ *
+ * @param cw
+ * the ClassWriter to which this label belongs.
+ * @param nLocal
+ * the number of local variables.
+ * @param local
+ * the local variable types. Primitive types are represented by
+ * {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
+ * {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
+ * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
+ * {@link Opcodes#UNINITIALIZED_THIS} (long and double are
+ * represented by a single element). Reference types are
+ * represented by String objects (representing internal names),
+ * and uninitialized types by Label objects (this label
+ * designates the NEW instruction that created this uninitialized
+ * value).
+ * @param nStack
+ * the number of operand stack elements.
+ * @param stack
+ * the operand stack types (same format as the "local" array).
+ */
+ final void set(ClassWriter cw, final int nLocal, final Object[] local,
+ final int nStack, final Object[] stack) {
+ int i = convert(cw, nLocal, local, inputLocals);
+ while (i < local.length) {
+ inputLocals[i++] = TOP;
+ }
+ int nStackTop = 0;
+ for (int j = 0; j < nStack; ++j) {
+ if (stack[j] == Opcodes.LONG || stack[j] == Opcodes.DOUBLE) {
+ ++nStackTop;
+ }
+ }
+ inputStack = new int[nStack + nStackTop];
+ convert(cw, nStack, stack, inputStack);
+ outputStackTop = 0;
+ initializationCount = 0;
+ }
+
+ /**
+ * Converts types from the MethodWriter.visitFrame() format to the Frame
+ * format.
+ *
+ * @param cw
+ * the ClassWriter to which this label belongs.
+ * @param nInput
+ * the number of types to convert.
+ * @param input
+ * the types to convert. Primitive types are represented by
+ * {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
+ * {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
+ * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
+ * {@link Opcodes#UNINITIALIZED_THIS} (long and double are
+ * represented by a single element). Reference types are
+ * represented by String objects (representing internal names),
+ * and uninitialized types by Label objects (this label
+ * designates the NEW instruction that created this uninitialized
+ * value).
+ * @param output
+ * where to store the converted types.
+ * @return the number of output elements.
+ */
+ private static int convert(ClassWriter cw, int nInput, Object[] input,
+ int[] output) {
+ int i = 0;
+ for (int j = 0; j < nInput; ++j) {
+ if (input[j] instanceof Integer) {
+ output[i++] = BASE | ((Integer) input[j]).intValue();
+ if (input[j] == Opcodes.LONG || input[j] == Opcodes.DOUBLE) {
+ output[i++] = TOP;
+ }
+ } else if (input[j] instanceof String) {
+ output[i++] = type(cw, Type.getObjectType((String) input[j])
+ .getDescriptor());
+ } else {
+ output[i++] = UNINITIALIZED
+ | cw.addUninitializedType("",
+ ((Label) input[j]).position);
+ }
+ }
+ return i;
+ }
+
+ /**
+ * Sets this frame to the value of the given frame. WARNING: after this
+ * method is called the two frames share the same data structures. It is
+ * recommended to discard the given frame f to avoid unexpected side
+ * effects.
+ *
+ * @param f
+ * The new frame value.
+ */
+ final void set(final Frame f) {
+ inputLocals = f.inputLocals;
+ inputStack = f.inputStack;
+ outputLocals = f.outputLocals;
+ outputStack = f.outputStack;
+ outputStackTop = f.outputStackTop;
+ initializationCount = f.initializationCount;
+ initializations = f.initializations;
+ }
+
/**
* Returns the output frame local variable type at the given index.
*
@@ -585,7 +689,7 @@ private void push(final int type) {
}
// pushes the type on the output stack
outputStack[outputStackTop++] = type;
- // updates the maximun height reached by the output stack, if needed
+ // updates the maximum height reached by the output stack, if needed
int top = owner.inputStackTop + outputStackTop;
if (top > owner.outputStackMax) {
owner.outputStackMax = top;
@@ -809,7 +913,7 @@ private int init(final ClassWriter cw, final int t) {
* @param maxLocals
* the maximum number of local variables of this method.
*/
- void initInputFrame(final ClassWriter cw, final int access,
+ final void initInputFrame(final ClassWriter cw, final int access,
final Type[] args, final int maxLocals) {
inputLocals = new int[maxLocals];
inputStack = new int[0];
@@ -1283,7 +1387,7 @@ void execute(final int opcode, final int arg, final ClassWriter cw,
* @return true if the input frame of the given label has been
* changed by this operation.
*/
- boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
+ final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
boolean changed = false;
int i, s, dim, kind, t;
@@ -1417,6 +1521,7 @@ private static boolean merge(final ClassWriter cw, int t,
// if t is the NULL type, merge(u,t)=u, so there is no change
return false;
} else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) {
+ // if t and u have the same dimension and same base kind
if ((u & BASE_KIND) == OBJECT) {
// if t is also a reference type, and if u and t have the
// same dimension merge(u,t) = dim(t) | common parent of the
@@ -1425,13 +1530,21 @@ private static boolean merge(final ClassWriter cw, int t,
| cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE);
} else {
// if u and t are array types, but not with the same element
- // type, merge(u,t)=java/lang/Object
- v = OBJECT | cw.addType("java/lang/Object");
+ // type, merge(u,t) = dim(u) - 1 | java/lang/Object
+ int vdim = ELEMENT_OF + (u & DIM);
+ v = vdim | OBJECT | cw.addType("java/lang/Object");
}
} else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) {
- // if t is any other reference or array type,
- // merge(u,t)=java/lang/Object
- v = OBJECT | cw.addType("java/lang/Object");
+ // if t is any other reference or array type, the merged type
+ // is min(udim, tdim) | java/lang/Object, where udim is the
+ // array dimension of u, minus 1 if u is an array type with a
+ // primitive element type (and similarly for tdim).
+ int tdim = (((t & DIM) == 0 || (t & BASE_KIND) == OBJECT) ? 0
+ : ELEMENT_OF) + (t & DIM);
+ int udim = (((u & DIM) == 0 || (u & BASE_KIND) == OBJECT) ? 0
+ : ELEMENT_OF) + (u & DIM);
+ v = Math.min(tdim, udim) | OBJECT
+ | cw.addType("java/lang/Object");
} else {
// if t is any other type, merge(u,t)=TOP
v = TOP;
diff --git a/src/java/nginx/clojure/asm/Handle.java b/src/java/nginx/clojure/asm/Handle.java
index c3ad856b..d92fe3d0 100644
--- a/src/java/nginx/clojure/asm/Handle.java
+++ b/src/java/nginx/clojure/asm/Handle.java
@@ -63,6 +63,12 @@ public final class Handle {
* The descriptor of the field or method designated by this handle.
*/
final String desc;
+
+
+ /**
+ * Indicate if the owner is an interface or not.
+ */
+ final boolean itf;
/**
* Constructs a new field or method handle.
@@ -84,14 +90,46 @@ public final class Handle {
* @param desc
* the descriptor of the field or method designated by this
* handle.
+ *
+ * @deprecated this constructor has been superseded
+ * by {@link #Handle(int, String, String, String, boolean)}.
*/
+ @Deprecated
public Handle(int tag, String owner, String name, String desc) {
+ this(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE);
+ }
+
+ /**
+ * Constructs a new field or method handle.
+ *
+ * @param tag
+ * the kind of field or method designated by this Handle. Must be
+ * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
+ * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
+ * {@link Opcodes#H_INVOKEVIRTUAL},
+ * {@link Opcodes#H_INVOKESTATIC},
+ * {@link Opcodes#H_INVOKESPECIAL},
+ * {@link Opcodes#H_NEWINVOKESPECIAL} or
+ * {@link Opcodes#H_INVOKEINTERFACE}.
+ * @param owner
+ * the internal name of the class that owns the field or method
+ * designated by this handle.
+ * @param name
+ * the name of the field or method designated by this handle.
+ * @param desc
+ * the descriptor of the field or method designated by this
+ * handle.
+ * @param itf
+ * true if the owner is an interface.
+ */
+ public Handle(int tag, String owner, String name, String desc, boolean itf) {
this.tag = tag;
this.owner = owner;
this.name = name;
this.desc = desc;
+ this.itf = itf;
}
-
+
/**
* Returns the kind of field or method designated by this handle.
*
@@ -134,6 +172,17 @@ public String getName() {
public String getDesc() {
return desc;
}
+
+ /**
+ * Returns true if the owner of the field or method designated
+ * by this handle is an interface.
+ *
+ * @return true if the owner of the field or method designated
+ * by this handle is an interface.
+ */
+ public boolean isInterface() {
+ return itf;
+ }
@Override
public boolean equals(Object obj) {
@@ -144,13 +193,13 @@ public boolean equals(Object obj) {
return false;
}
Handle h = (Handle) obj;
- return tag == h.tag && owner.equals(h.owner) && name.equals(h.name)
- && desc.equals(h.desc);
+ return tag == h.tag && itf == h.itf && owner.equals(h.owner)
+ && name.equals(h.name) && desc.equals(h.desc);
}
@Override
public int hashCode() {
- return tag + owner.hashCode() * name.hashCode() * desc.hashCode();
+ return tag + (itf? 64: 0) + owner.hashCode() * name.hashCode() * desc.hashCode();
}
/**
@@ -158,13 +207,16 @@ public int hashCode() {
* representation is:
*
*
+ * for a reference to a class:
* owner '.' name desc ' ' '(' tag ')'
+ * for a reference to an interface:
+ * owner '.' name desc ' ' '(' tag ' ' itf ')'
*
*
* . As this format is unambiguous, it can be parsed if necessary.
*/
@Override
public String toString() {
- return owner + '.' + name + desc + " (" + tag + ')';
+ return owner + '.' + name + desc + " (" + tag + (itf? " itf": "") + ')';
}
}
diff --git a/src/java/nginx/clojure/asm/Item.java b/src/java/nginx/clojure/asm/Item.java
index 316efdc7..bfdb0c4f 100644
--- a/src/java/nginx/clojure/asm/Item.java
+++ b/src/java/nginx/clojure/asm/Item.java
@@ -201,6 +201,7 @@ void set(final double doubleVal) {
* @param strVal3
* third part of the value of this item.
*/
+ @SuppressWarnings("fallthrough")
void set(final int type, final String strVal1, final String strVal2,
final String strVal3) {
this.type = type;
@@ -208,9 +209,10 @@ void set(final int type, final String strVal1, final String strVal2,
this.strVal2 = strVal2;
this.strVal3 = strVal3;
switch (type) {
+ case ClassWriter.CLASS:
+ this.intVal = 0; // intVal of a class must be zero, see visitInnerClass
case ClassWriter.UTF8:
case ClassWriter.STR:
- case ClassWriter.CLASS:
case ClassWriter.MTYPE:
case ClassWriter.TYPE_NORMAL:
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
diff --git a/src/java/nginx/clojure/asm/Label.java b/src/java/nginx/clojure/asm/Label.java
index c0a366da..fd0b1f96 100644
--- a/src/java/nginx/clojure/asm/Label.java
+++ b/src/java/nginx/clojure/asm/Label.java
@@ -111,7 +111,7 @@ public class Label {
* Field used to associate user information to a label. Warning: this field
* is used by the ASM tree package. In order to use it with the ASM tree
* package you must override the
- * {@link org.objectweb.asm.tree.MethodNode#getLabelNode} method.
+ * {@link nginx.clojure.asm.tree.MethodNode#getLabelNode} method.
*/
public Object info;
@@ -131,7 +131,11 @@ public class Label {
int status;
/**
- * The line number corresponding to this label, if known.
+ * The line number corresponding to this label, if known. If there are
+ * several lines, each line is stored in a separate label, all linked via
+ * their next field (these links are created in ClassReader and removed just
+ * before visitLabel is called, so that this does not impact the rest of the
+ * code).
*/
int line;
@@ -239,7 +243,8 @@ public class Label {
* The next basic block in the basic block stack. This stack is used in the
* main loop of the fix point algorithm used in the second step of the
* control flow analysis algorithms. It is also used in
- * {@link #visitSubroutine} to avoid using a recursive method.
+ * {@link #visitSubroutine} to avoid using a recursive method, and in
+ * ClassReader to temporarily store multiple source lines for a label.
*
* @see MethodWriter#visitMaxs
*/
@@ -359,9 +364,8 @@ private void addReference(final int sourcePosition,
* small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo
- * instructions will need to be replaced with true instructions with
- * wider offsets (4 bytes instead of 2). This is done in
- * {@link MethodWriter#resizeInstructions}.
+ * instructions will be replaced with standard bytecode instructions
+ * with wider offsets (4 bytes instead of 2), in ClassReader.
* @throws IllegalArgumentException
* if this label has already been resolved, or if it has not
* been created by the given code writer.
@@ -473,7 +477,7 @@ boolean inSameSubroutine(final Label block) {
void addToSubroutine(final long id, final int nbSubroutines) {
if ((status & VISITED) == 0) {
status |= VISITED;
- srcAndRefPositions = new int[(nbSubroutines - 1) / 32 + 1];
+ srcAndRefPositions = new int[nbSubroutines / 32 + 1];
}
srcAndRefPositions[(int) (id >>> 32)] |= (int) id;
}
diff --git a/src/java/nginx/clojure/asm/MethodVisitor.java b/src/java/nginx/clojure/asm/MethodVisitor.java
index 6698ecdb..1927b682 100644
--- a/src/java/nginx/clojure/asm/MethodVisitor.java
+++ b/src/java/nginx/clojure/asm/MethodVisitor.java
@@ -31,18 +31,25 @@
/**
* A visitor to visit a Java method. The methods of this class must be called in
- * the following order: [ visitAnnotationDefault ] (
- * visitAnnotation | visitParameterAnnotation |
+ * the following order: ( visitParameter )* [
+ * visitAnnotationDefault ] ( visitAnnotation |
+ * visitParameterAnnotationvisitTypeAnnotation |
* visitAttribute )* [ visitCode ( visitFrame |
- * visitXInsn | visitLabel |
- * visitTryCatchBlock | visitLocalVariable |
- * visitLineNumber )* visitMaxs ] visitEnd. In
- * addition, the visitXInsn and visitLabel methods
- * must be called in the sequential order of the bytecode instructions of the
- * visited code, visitTryCatchBlock must be called before the
- * labels passed as arguments have been visited, and the
- * visitLocalVariable and visitLineNumber methods must be
- * called after the labels passed as arguments have been visited.
+ * visitXInsn | visitLabel |
+ * visitInsnAnnotation | visitTryCatchBlock |
+ * visitTryCatchAnnotation | visitLocalVariable |
+ * visitLocalVariableAnnotation | visitLineNumber )*
+ * visitMaxs ] visitEnd. In addition, the
+ * visitXInsn and visitLabel methods must be called in
+ * the sequential order of the bytecode instructions of the visited code,
+ * visitInsnAnnotation must be called after the annotated
+ * instruction, visitTryCatchBlock must be called before the
+ * labels passed as arguments have been visited,
+ * visitTryCatchBlockAnnotation must be called after the
+ * corresponding try catch block has been visited, and the
+ * visitLocalVariable, visitLocalVariableAnnotation and
+ * visitLineNumber methods must be called after the labels
+ * passed as arguments have been visited.
*
* @author Eric Bruneton
*/
@@ -50,7 +57,7 @@ public abstract class MethodVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
- * must be one of {@link Opcodes#ASM4}.
+ * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
*/
protected final int api;
@@ -65,7 +72,7 @@ public abstract class MethodVisitor {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
*/
public MethodVisitor(final int api) {
this(api, null);
@@ -76,13 +83,13 @@ public MethodVisitor(final int api) {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param mv
* the method visitor to which this visitor must delegate method
* calls. May be null.
*/
public MethodVisitor(final int api, final MethodVisitor mv) {
- if (api != Opcodes.ASM4) {
+ if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
throw new IllegalArgumentException();
}
this.api = api;
@@ -90,9 +97,28 @@ public MethodVisitor(final int api, final MethodVisitor mv) {
}
// -------------------------------------------------------------------------
- // Annotations and non standard attributes
+ // Parameters, annotations and non standard attributes
// -------------------------------------------------------------------------
+ /**
+ * Visits a parameter of this method.
+ *
+ * @param name
+ * parameter name or null if none is provided.
+ * @param access
+ * the parameter's access flags, only ACC_FINAL,
+ * ACC_SYNTHETIC or/and ACC_MANDATED are
+ * allowed (see {@link Opcodes}).
+ */
+ public void visitParameter(String name, int access) {
+ if (api < Opcodes.ASM5) {
+ throw new RuntimeException();
+ }
+ if (mv != null) {
+ mv.visitParameter(name, access);
+ }
+ }
+
/**
* Visits the default value of this annotation interface method.
*
@@ -127,6 +153,42 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
+ /**
+ * Visits an annotation on a type in the method signature.
+ *
+ * @param typeRef
+ * a reference to the annotated type. The sort of this type
+ * reference must be {@link TypeReference#METHOD_TYPE_PARAMETER
+ * METHOD_TYPE_PARAMETER},
+ * {@link TypeReference#METHOD_TYPE_PARAMETER_BOUND
+ * METHOD_TYPE_PARAMETER_BOUND},
+ * {@link TypeReference#METHOD_RETURN METHOD_RETURN},
+ * {@link TypeReference#METHOD_RECEIVER METHOD_RECEIVER},
+ * {@link TypeReference#METHOD_FORMAL_PARAMETER
+ * METHOD_FORMAL_PARAMETER} or {@link TypeReference#THROWS
+ * THROWS}. See {@link TypeReference}.
+ * @param typePath
+ * the path to the annotated type argument, wildcard bound, array
+ * element type, or static inner type within 'typeRef'. May be
+ * null if the annotation targets 'typeRef' as a whole.
+ * @param desc
+ * the class descriptor of the annotation class.
+ * @param visible
+ * true if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or null if
+ * this visitor is not interested in visiting this annotation.
+ */
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ if (api < Opcodes.ASM5) {
+ throw new RuntimeException();
+ }
+ if (mv != null) {
+ return mv.visitTypeAnnotation(typeRef, typePath, desc, visible);
+ }
+ return null;
+ }
+
/**
* Visits an annotation of a parameter this method.
*
@@ -201,9 +263,11 @@ public void visitCode() {
*
{@link Opcodes#F_CHOP} representing frame with current locals are the
* same as the locals in the previous frame, except that the last 1-3 locals
* are absent and with the empty stack (nLocals is 1, 2 or 3).
+ *
+ *
*
- *
+ *
* In both cases the first frame, corresponding to the method's parameters
* and access flags, is implicit and must not be visited. Also, it is
* illegal to visit two or more frames for the same code location (i.e., at
@@ -376,13 +440,52 @@ public void visitFieldInsn(int opcode, String owner, String name,
* @param desc
* the method's descriptor (see {@link Type Type}).
*/
+ @Deprecated
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
+ if (api >= Opcodes.ASM5) {
+ boolean itf = opcode == Opcodes.INVOKEINTERFACE;
+ visitMethodInsn(opcode, owner, name, desc, itf);
+ return;
+ }
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc);
}
}
+ /**
+ * Visits a method instruction. A method instruction is an instruction that
+ * invokes a method.
+ *
+ * @param opcode
+ * the opcode of the type instruction to be visited. This opcode
+ * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or
+ * INVOKEINTERFACE.
+ * @param owner
+ * the internal name of the method's owner class (see
+ * {@link Type#getInternalName() getInternalName}).
+ * @param name
+ * the method's name.
+ * @param desc
+ * the method's descriptor (see {@link Type Type}).
+ * @param itf
+ * if the method's owner class is an interface.
+ */
+ public void visitMethodInsn(int opcode, String owner, String name,
+ String desc, boolean itf) {
+ if (api < Opcodes.ASM5) {
+ if (itf != (opcode == Opcodes.INVOKEINTERFACE)) {
+ throw new IllegalArgumentException(
+ "INVOKESPECIAL/STATIC on interfaces require ASM 5");
+ }
+ visitMethodInsn(opcode, owner, name, desc);
+ return;
+ }
+ if (mv != null) {
+ mv.visitMethodInsn(opcode, owner, name, desc, itf);
+ }
+ }
+
/**
* Visits an invokedynamic instruction.
*
@@ -558,6 +661,48 @@ public void visitMultiANewArrayInsn(String desc, int dims) {
}
}
+ /**
+ * Visits an annotation on an instruction. This method must be called just
+ * after the annotated instruction. It can be called several times
+ * for the same instruction.
+ *
+ * @param typeRef
+ * a reference to the annotated type. The sort of this type
+ * reference must be {@link TypeReference#INSTANCEOF INSTANCEOF},
+ * {@link TypeReference#NEW NEW},
+ * {@link TypeReference#CONSTRUCTOR_REFERENCE
+ * CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE
+ * METHOD_REFERENCE}, {@link TypeReference#CAST CAST},
+ * {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
+ * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
+ * {@link TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT
+ * METHOD_INVOCATION_TYPE_ARGUMENT},
+ * {@link TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
+ * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
+ * {@link TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT
+ * METHOD_REFERENCE_TYPE_ARGUMENT}. See {@link TypeReference}.
+ * @param typePath
+ * the path to the annotated type argument, wildcard bound, array
+ * element type, or static inner type within 'typeRef'. May be
+ * null if the annotation targets 'typeRef' as a whole.
+ * @param desc
+ * the class descriptor of the annotation class.
+ * @param visible
+ * true if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or null if
+ * this visitor is not interested in visiting this annotation.
+ */
+ public AnnotationVisitor visitInsnAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ if (api < Opcodes.ASM5) {
+ throw new RuntimeException();
+ }
+ if (mv != null) {
+ return mv.visitInsnAnnotation(typeRef, typePath, desc, visible);
+ }
+ return null;
+ }
+
// -------------------------------------------------------------------------
// Exceptions table entries, debug information, max stack and max locals
// -------------------------------------------------------------------------
@@ -586,6 +731,38 @@ public void visitTryCatchBlock(Label start, Label end, Label handler,
}
}
+ /**
+ * Visits an annotation on an exception handler type. This method must be
+ * called after the {@link #visitTryCatchBlock} for the annotated
+ * exception handler. It can be called several times for the same exception
+ * handler.
+ *
+ * @param typeRef
+ * a reference to the annotated type. The sort of this type
+ * reference must be {@link TypeReference#EXCEPTION_PARAMETER
+ * EXCEPTION_PARAMETER}. See {@link TypeReference}.
+ * @param typePath
+ * the path to the annotated type argument, wildcard bound, array
+ * element type, or static inner type within 'typeRef'. May be
+ * null if the annotation targets 'typeRef' as a whole.
+ * @param desc
+ * the class descriptor of the annotation class.
+ * @param visible
+ * true if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or null if
+ * this visitor is not interested in visiting this annotation.
+ */
+ public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ if (api < Opcodes.ASM5) {
+ throw new RuntimeException();
+ }
+ if (mv != null) {
+ return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
+ }
+ return null;
+ }
+
/**
* Visits a local variable declaration.
*
@@ -616,6 +793,48 @@ public void visitLocalVariable(String name, String desc, String signature,
}
}
+ /**
+ * Visits an annotation on a local variable type.
+ *
+ * @param typeRef
+ * a reference to the annotated type. The sort of this type
+ * reference must be {@link TypeReference#LOCAL_VARIABLE
+ * LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE
+ * RESOURCE_VARIABLE}. See {@link TypeReference}.
+ * @param typePath
+ * the path to the annotated type argument, wildcard bound, array
+ * element type, or static inner type within 'typeRef'. May be
+ * null if the annotation targets 'typeRef' as a whole.
+ * @param start
+ * the fist instructions corresponding to the continuous ranges
+ * that make the scope of this local variable (inclusive).
+ * @param end
+ * the last instructions corresponding to the continuous ranges
+ * that make the scope of this local variable (exclusive). This
+ * array must have the same size as the 'start' array.
+ * @param index
+ * the local variable's index in each range. This array must have
+ * the same size as the 'start' array.
+ * @param desc
+ * the class descriptor of the annotation class.
+ * @param visible
+ * true if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or null if
+ * this visitor is not interested in visiting this annotation.
+ */
+ public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
+ TypePath typePath, Label[] start, Label[] end, int[] index,
+ String desc, boolean visible) {
+ if (api < Opcodes.ASM5) {
+ throw new RuntimeException();
+ }
+ if (mv != null) {
+ return mv.visitLocalVariableAnnotation(typeRef, typePath, start,
+ end, index, desc, visible);
+ }
+ return null;
+ }
+
/**
* Visits a line number declaration.
*
diff --git a/src/java/nginx/clojure/asm/MethodWriter.java b/src/java/nginx/clojure/asm/MethodWriter.java
index f02e2fdb..3fe7ce46 100644
--- a/src/java/nginx/clojure/asm/MethodWriter.java
+++ b/src/java/nginx/clojure/asm/MethodWriter.java
@@ -43,7 +43,7 @@ class MethodWriter extends MethodVisitor {
* Pseudo access flag used to denote constructors.
*/
static final int ACC_CONSTRUCTOR = 0x80000;
-
+
/**
* Frame has exactly the same locals as the previous stack map frame and
* number of stack items is zero.
@@ -99,7 +99,19 @@ class MethodWriter extends MethodVisitor {
*
* @see #compute
*/
- private static final int FRAMES = 0;
+ static final int FRAMES = 0;
+
+ /**
+ * Indicates that the stack map frames of type F_INSERT must be computed.
+ * The other frames are not (re)computed. They should all be of type F_NEW
+ * and should be sufficient to compute the content of the F_INSERT frames,
+ * together with the bytecode instructions between a F_NEW and a F_INSERT
+ * frame - and without any knowledge of the type hierarchy (by definition of
+ * F_INSERT).
+ *
+ * @see #compute
+ */
+ static final int INSERTED_FRAMES = 1;
/**
* Indicates that the maximum stack size and number of local variables must
@@ -107,14 +119,14 @@ class MethodWriter extends MethodVisitor {
*
* @see #compute
*/
- private static final int MAXS = 1;
+ static final int MAXS = 2;
/**
* Indicates that nothing must be automatically computed.
*
* @see #compute
*/
- private static final int NOTHING = 2;
+ static final int NOTHING = 3;
/**
* The class writer to which this method must be added.
@@ -191,6 +203,18 @@ class MethodWriter extends MethodVisitor {
*/
private AnnotationWriter ianns;
+ /**
+ * The runtime visible type annotations of this method. May be null
+ * .
+ */
+ private AnnotationWriter tanns;
+
+ /**
+ * The runtime invisible type annotations of this method. May be
+ * null.
+ */
+ private AnnotationWriter itanns;
+
/**
* The runtime visible parameter annotations of this method. May be
* null.
@@ -282,6 +306,16 @@ class MethodWriter extends MethodVisitor {
*/
private Handler lastHandler;
+ /**
+ * Number of entries in the MethodParameters attribute.
+ */
+ private int methodParametersCount;
+
+ /**
+ * The MethodParameters attribute.
+ */
+ private ByteVector methodParameters;
+
/**
* Number of entries in the LocalVariableTable attribute.
*/
@@ -313,14 +347,24 @@ class MethodWriter extends MethodVisitor {
private ByteVector lineNumber;
/**
- * The non standard attributes of the method's code.
+ * The start offset of the last visited instruction.
*/
- private Attribute cattrs;
+ private int lastCodeOffset;
/**
- * Indicates if some jump instructions are too small and need to be resized.
+ * The runtime visible type annotations of the code. May be null.
*/
- private boolean resize;
+ private AnnotationWriter ctanns;
+
+ /**
+ * The runtime invisible type annotations of the code. May be null.
+ */
+ private AnnotationWriter ictanns;
+
+ /**
+ * The non standard attributes of the method's code.
+ */
+ private Attribute cattrs;
/**
* The number of subroutines in this method.
@@ -343,6 +387,7 @@ class MethodWriter extends MethodVisitor {
* Indicates what must be automatically computed.
*
* @see #FRAMES
+ * @see #INSERTED_FRAMES
* @see #MAXS
* @see #NOTHING
*/
@@ -405,18 +450,13 @@ class MethodWriter extends MethodVisitor {
* @param exceptions
* the internal names of the method's exceptions. May be
* null.
- * @param computeMaxs
- * true if the maximum stack size and number of local
- * variables must be automatically computed.
- * @param computeFrames
- * true if the stack map tables must be recomputed from
- * scratch.
+ * @param compute
+ * Indicates what must be automatically computed (see #compute).
*/
MethodWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature,
- final String[] exceptions, final boolean computeMaxs,
- final boolean computeFrames) {
- super(Opcodes.ASM4);
+ final String[] exceptions, final int compute) {
+ super(Opcodes.ASM5);
if (cw.firstMethod == null) {
cw.firstMethod = this;
} else {
@@ -441,8 +481,8 @@ class MethodWriter extends MethodVisitor {
this.exceptions[i] = cw.newClass(exceptions[i]);
}
}
- this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
- if (computeMaxs || computeFrames) {
+ this.compute = compute;
+ if (compute != NOTHING) {
// updates maxLocals
int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
if ((access & Opcodes.ACC_STATIC) != 0) {
@@ -461,6 +501,16 @@ class MethodWriter extends MethodVisitor {
// Implementation of the MethodVisitor abstract class
// ------------------------------------------------------------------------
+ @Override
+ public void visitParameter(String name, int access) {
+ if (methodParameters == null) {
+ methodParameters = new ByteVector();
+ }
+ ++methodParametersCount;
+ methodParameters.putShort((name == null) ? 0 : cw.newUTF8(name))
+ .putShort(access);
+ }
+
@Override
public AnnotationVisitor visitAnnotationDefault() {
if (!ClassReader.ANNOTATIONS) {
@@ -490,6 +540,29 @@ public AnnotationVisitor visitAnnotation(final String desc,
return aw;
}
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(final int typeRef,
+ final TypePath typePath, final String desc, final boolean visible) {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ // write target_type and target_info
+ AnnotationWriter.putTarget(typeRef, typePath, bv);
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
+ bv.length - 2);
+ if (visible) {
+ aw.next = tanns;
+ tanns = aw;
+ } else {
+ aw.next = itanns;
+ itanns = aw;
+ }
+ return aw;
+ }
+
@Override
public AnnotationVisitor visitParameterAnnotation(final int parameter,
final String desc, final boolean visible) {
@@ -544,7 +617,29 @@ public void visitFrame(final int type, final int nLocal,
return;
}
- if (type == Opcodes.F_NEW) {
+ if (compute == INSERTED_FRAMES) {
+ if (currentBlock.frame == null) {
+ // This should happen only once, for the implicit first frame
+ // (which is explicitly visited in ClassReader if the
+ // EXPAND_ASM_INSNS option is used).
+ currentBlock.frame = new CurrentFrame();
+ currentBlock.frame.owner = currentBlock;
+ currentBlock.frame.initInputFrame(cw, access,
+ Type.getArgumentTypes(descriptor), nLocal);
+ visitImplicitFirstFrame();
+ } else {
+ if (type == Opcodes.F_NEW) {
+ currentBlock.frame.set(cw, nLocal, local, nStack, stack);
+ } else {
+ // In this case type is equal to F_INSERT by hypothesis, and
+ // currentBlock.frame contains the stack map frame at the
+ // current instruction, computed from the last F_NEW frame
+ // and the bytecode instructions in between (via calls to
+ // CurrentFrame#execute).
+ }
+ visitFrame(currentBlock.frame);
+ }
+ } else if (type == Opcodes.F_NEW) {
if (previousFrame == null) {
visitImplicitFirstFrame();
}
@@ -642,12 +737,13 @@ public void visitFrame(final int type, final int nLocal,
@Override
public void visitInsn(final int opcode) {
+ lastCodeOffset = code.length;
// adds the instruction to the bytecode of the method
code.putByte(opcode);
// update currentBlock
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, null, null);
} else {
// updates current and max stack sizes
@@ -667,9 +763,10 @@ public void visitInsn(final int opcode) {
@Override
public void visitIntInsn(final int opcode, final int operand) {
+ lastCodeOffset = code.length;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, operand, null, null);
} else if (opcode != Opcodes.NEWARRAY) {
// updates current and max stack sizes only for NEWARRAY
@@ -691,9 +788,10 @@ public void visitIntInsn(final int opcode, final int operand) {
@Override
public void visitVarInsn(final int opcode, final int var) {
+ lastCodeOffset = code.length;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, var, null, null);
} else {
// updates current and max stack sizes
@@ -749,10 +847,11 @@ public void visitVarInsn(final int opcode, final int var) {
@Override
public void visitTypeInsn(final int opcode, final String type) {
+ lastCodeOffset = code.length;
Item i = cw.newClassItem(type);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, code.length, cw, i);
} else if (opcode == Opcodes.NEW) {
// updates current and max stack sizes only if opcode == NEW
@@ -771,10 +870,11 @@ public void visitTypeInsn(final int opcode, final String type) {
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
+ lastCodeOffset = code.length;
Item i = cw.newFieldItem(owner, name, desc);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, cw, i);
} else {
int size;
@@ -808,13 +908,13 @@ public void visitFieldInsn(final int opcode, final String owner,
@Override
public void visitMethodInsn(final int opcode, final String owner,
- final String name, final String desc) {
- boolean itf = opcode == Opcodes.INVOKEINTERFACE;
+ final String name, final String desc, final boolean itf) {
+ lastCodeOffset = code.length;
Item i = cw.newMethodItem(owner, name, desc, itf);
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, cw, i);
} else {
/*
@@ -847,7 +947,7 @@ public void visitMethodInsn(final int opcode, final String owner,
}
}
// adds the instruction to the bytecode of the method
- if (itf) {
+ if (opcode == Opcodes.INVOKEINTERFACE) {
if (argSize == 0) {
argSize = Type.getArgumentsAndReturnSizes(desc);
i.intVal = argSize;
@@ -861,11 +961,12 @@ public void visitMethodInsn(final int opcode, final String owner,
@Override
public void visitInvokeDynamicInsn(final String name, final String desc,
final Handle bsm, final Object... bsmArgs) {
+ lastCodeOffset = code.length;
Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs);
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i);
} else {
/*
@@ -899,7 +1000,10 @@ public void visitInvokeDynamicInsn(final String name, final String desc,
}
@Override
- public void visitJumpInsn(final int opcode, final Label label) {
+ public void visitJumpInsn(int opcode, final Label label) {
+ boolean isWide = opcode >= 200; // GOTO_W
+ opcode = isWide ? opcode - 33 : opcode;
+ lastCodeOffset = code.length;
Label nextInsn = null;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
@@ -913,6 +1017,8 @@ public void visitJumpInsn(final int opcode, final Label label) {
// creates a Label for the next basic block
nextInsn = new Label();
}
+ } else if (compute == INSERTED_FRAMES) {
+ currentBlock.frame.execute(opcode, 0, null, null);
} else {
if (opcode == Opcodes.JSR) {
if ((label.status & Label.SUBROUTINE) == 0) {
@@ -964,6 +1070,14 @@ public void visitJumpInsn(final int opcode, final Label label) {
code.putByte(200); // GOTO_W
}
label.put(this, code, code.length - 1, true);
+ } else if (isWide) {
+ /*
+ * case of a GOTO_W or JSR_W specified by the user (normally
+ * ClassReader when used to resize instructions). In this case we
+ * keep the original instruction.
+ */
+ code.putByte(opcode + 33);
+ label.put(this, code, code.length - 1, true);
} else {
/*
* case of a backward jump with an offset >= -32768, or of a forward
@@ -991,7 +1105,7 @@ public void visitJumpInsn(final int opcode, final Label label) {
@Override
public void visitLabel(final Label label) {
// resolves previous forward references to label, if any
- resize |= label.resolve(this, code.length, code.data);
+ cw.hasAsmInsns |= label.resolve(this, code.length, code.data);
// updates currentBlock
if ((label.status & Label.DEBUG) != 0) {
return;
@@ -1024,6 +1138,18 @@ public void visitLabel(final Label label) {
previousBlock.successor = label;
}
previousBlock = label;
+ } else if (compute == INSERTED_FRAMES) {
+ if (currentBlock == null) {
+ // This case should happen only once, for the visitLabel call in
+ // the constructor. Indeed, if compute is equal to
+ // INSERTED_FRAMES currentBlock can not be set back to null (see
+ // #noSuccessor).
+ currentBlock = label;
+ } else {
+ // Updates the frame owner so that a correct frame offset is
+ // computed in visitFrame(Frame).
+ currentBlock.frame.owner = label;
+ }
} else if (compute == MAXS) {
if (currentBlock != null) {
// ends current block (with one new successor)
@@ -1045,10 +1171,11 @@ public void visitLabel(final Label label) {
@Override
public void visitLdcInsn(final Object cst) {
+ lastCodeOffset = code.length;
Item i = cw.newConstItem(cst);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
} else {
int size;
@@ -1078,8 +1205,9 @@ public void visitLdcInsn(final Object cst) {
@Override
public void visitIincInsn(final int var, final int increment) {
+ lastCodeOffset = code.length;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.IINC, var, null, null);
}
}
@@ -1102,6 +1230,7 @@ public void visitIincInsn(final int var, final int increment) {
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
+ lastCodeOffset = code.length;
// adds the instruction to the bytecode of the method
int source = code.length;
code.putByte(Opcodes.TABLESWITCH);
@@ -1118,6 +1247,7 @@ public void visitTableSwitchInsn(final int min, final int max,
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
+ lastCodeOffset = code.length;
// adds the instruction to the bytecode of the method
int source = code.length;
code.putByte(Opcodes.LOOKUPSWITCH);
@@ -1160,10 +1290,11 @@ private void visitSwitchInsn(final Label dflt, final Label[] labels) {
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ lastCodeOffset = code.length;
Item i = cw.newClassItem(desc);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
} else {
// updates current stack size (max stack size unchanged because
@@ -1175,6 +1306,30 @@ public void visitMultiANewArrayInsn(final String desc, final int dims) {
code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);
}
+ @Override
+ public AnnotationVisitor visitInsnAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ // write target_type and target_info
+ typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8);
+ AnnotationWriter.putTarget(typeRef, typePath, bv);
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
+ bv.length - 2);
+ if (visible) {
+ aw.next = ctanns;
+ ctanns = aw;
+ } else {
+ aw.next = ictanns;
+ ictanns = aw;
+ }
+ return aw;
+ }
+
@Override
public void visitTryCatchBlock(final Label start, final Label end,
final Label handler, final String type) {
@@ -1193,6 +1348,29 @@ public void visitTryCatchBlock(final Label start, final Label end,
lastHandler = h;
}
+ @Override
+ public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ // write target_type and target_info
+ AnnotationWriter.putTarget(typeRef, typePath, bv);
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
+ bv.length - 2);
+ if (visible) {
+ aw.next = ctanns;
+ ctanns = aw;
+ } else {
+ aw.next = ictanns;
+ ictanns = aw;
+ }
+ return aw;
+ }
+
@Override
public void visitLocalVariable(final String name, final String desc,
final String signature, final Label start, final Label end,
@@ -1225,6 +1403,41 @@ public void visitLocalVariable(final String name, final String desc,
}
}
+ @Override
+ public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
+ TypePath typePath, Label[] start, Label[] end, int[] index,
+ String desc, boolean visible) {
+ if (!ClassReader.ANNOTATIONS) {
+ return null;
+ }
+ ByteVector bv = new ByteVector();
+ // write target_type and target_info
+ bv.putByte(typeRef >>> 24).putShort(start.length);
+ for (int i = 0; i < start.length; ++i) {
+ bv.putShort(start[i].position)
+ .putShort(end[i].position - start[i].position)
+ .putShort(index[i]);
+ }
+ if (typePath == null) {
+ bv.putByte(0);
+ } else {
+ int length = typePath.b[typePath.offset] * 2 + 1;
+ bv.putByteArray(typePath.b, typePath.offset, length);
+ }
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
+ bv.length - 2);
+ if (visible) {
+ aw.next = ctanns;
+ ctanns = aw;
+ } else {
+ aw.next = ictanns;
+ ictanns = aw;
+ }
+ return aw;
+ }
+
@Override
public void visitLineNumber(final int line, final Label start) {
if (lineNumber == null) {
@@ -1267,8 +1480,8 @@ public void visitMaxs(final int maxStack, final int maxLocals) {
// creates and visits the first (implicit) frame
Frame f = labels.frame;
- Type[] args = Type.getArgumentTypes(descriptor);
- f.initInputFrame(cw, access, args, this.maxLocals);
+ f.initInputFrame(cw, access, Type.getArgumentTypes(descriptor),
+ this.maxLocals);
visitFrame(f);
/*
@@ -1516,7 +1729,9 @@ private void noSuccessor() {
} else {
currentBlock.outputStackMax = maxStackSize;
}
- currentBlock = null;
+ if (compute != INSERTED_FRAMES) {
+ currentBlock = null;
+ }
}
// ------------------------------------------------------------------------
@@ -1794,43 +2009,43 @@ private void writeFrameTypes(final int start, final int end) {
stackMap.putByte(v);
}
} else {
- StringBuffer buf = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
d >>= 28;
while (d-- > 0) {
- buf.append('[');
+ sb.append('[');
}
if ((t & Frame.BASE_KIND) == Frame.OBJECT) {
- buf.append('L');
- buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);
- buf.append(';');
+ sb.append('L');
+ sb.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);
+ sb.append(';');
} else {
switch (t & 0xF) {
case 1:
- buf.append('I');
+ sb.append('I');
break;
case 2:
- buf.append('F');
+ sb.append('F');
break;
case 3:
- buf.append('D');
+ sb.append('D');
break;
case 9:
- buf.append('Z');
+ sb.append('Z');
break;
case 10:
- buf.append('B');
+ sb.append('B');
break;
case 11:
- buf.append('C');
+ sb.append('C');
break;
case 12:
- buf.append('S');
+ sb.append('S');
break;
default:
- buf.append('J');
+ sb.append('J');
}
}
- stackMap.putByte(7).putShort(cw.newClass(buf.toString()));
+ stackMap.putByte(7).putShort(cw.newClass(sb.toString()));
}
}
}
@@ -1858,17 +2073,9 @@ final int getSize() {
if (classReaderOffset != 0) {
return 6 + classReaderLength;
}
- if (resize) {
- // replaces the temporary jump opcodes introduced by Label.resolve.
- if (ClassReader.RESIZE) {
- resizeInstructions();
- } else {
- throw new RuntimeException("Method code too large!");
- }
- }
int size = 8;
if (code.length > 0) {
- if (code.length > 65536) {
+ if (code.length > 65535) {
throw new RuntimeException("Method code too large!");
}
cw.newUTF8("Code");
@@ -1890,6 +2097,14 @@ final int getSize() {
cw.newUTF8(zip ? "StackMapTable" : "StackMap");
size += 8 + stackMap.length;
}
+ if (ClassReader.ANNOTATIONS && ctanns != null) {
+ cw.newUTF8("RuntimeVisibleTypeAnnotations");
+ size += 8 + ctanns.getSize();
+ }
+ if (ClassReader.ANNOTATIONS && ictanns != null) {
+ cw.newUTF8("RuntimeInvisibleTypeAnnotations");
+ size += 8 + ictanns.getSize();
+ }
if (cattrs != null) {
size += cattrs.getSize(cw, code.data, code.length, maxStack,
maxLocals);
@@ -1915,6 +2130,10 @@ final int getSize() {
cw.newUTF8(signature);
size += 8;
}
+ if (methodParameters != null) {
+ cw.newUTF8("MethodParameters");
+ size += 7 + methodParameters.length;
+ }
if (ClassReader.ANNOTATIONS && annd != null) {
cw.newUTF8("AnnotationDefault");
size += 6 + annd.length;
@@ -1927,6 +2146,14 @@ final int getSize() {
cw.newUTF8("RuntimeInvisibleAnnotations");
size += 8 + ianns.getSize();
}
+ if (ClassReader.ANNOTATIONS && tanns != null) {
+ cw.newUTF8("RuntimeVisibleTypeAnnotations");
+ size += 8 + tanns.getSize();
+ }
+ if (ClassReader.ANNOTATIONS && itanns != null) {
+ cw.newUTF8("RuntimeInvisibleTypeAnnotations");
+ size += 8 + itanns.getSize();
+ }
if (ClassReader.ANNOTATIONS && panns != null) {
cw.newUTF8("RuntimeVisibleParameterAnnotations");
size += 7 + 2 * (panns.length - synthetics);
@@ -1983,6 +2210,9 @@ final void put(final ByteVector out) {
if (ClassReader.SIGNATURES && signature != null) {
++attributeCount;
}
+ if (methodParameters != null) {
+ ++attributeCount;
+ }
if (ClassReader.ANNOTATIONS && annd != null) {
++attributeCount;
}
@@ -1992,6 +2222,12 @@ final void put(final ByteVector out) {
if (ClassReader.ANNOTATIONS && ianns != null) {
++attributeCount;
}
+ if (ClassReader.ANNOTATIONS && tanns != null) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && itanns != null) {
+ ++attributeCount;
+ }
if (ClassReader.ANNOTATIONS && panns != null) {
++attributeCount;
}
@@ -2016,6 +2252,12 @@ final void put(final ByteVector out) {
if (stackMap != null) {
size += 8 + stackMap.length;
}
+ if (ClassReader.ANNOTATIONS && ctanns != null) {
+ size += 8 + ctanns.getSize();
+ }
+ if (ClassReader.ANNOTATIONS && ictanns != null) {
+ size += 8 + ictanns.getSize();
+ }
if (cattrs != null) {
size += cattrs.getSize(cw, code.data, code.length, maxStack,
maxLocals);
@@ -2045,6 +2287,12 @@ final void put(final ByteVector out) {
if (stackMap != null) {
++attributeCount;
}
+ if (ClassReader.ANNOTATIONS && ctanns != null) {
+ ++attributeCount;
+ }
+ if (ClassReader.ANNOTATIONS && ictanns != null) {
+ ++attributeCount;
+ }
if (cattrs != null) {
attributeCount += cattrs.getCount();
}
@@ -2070,6 +2318,14 @@ final void put(final ByteVector out) {
out.putInt(stackMap.length + 2).putShort(frameCount);
out.putByteArray(stackMap.data, 0, stackMap.length);
}
+ if (ClassReader.ANNOTATIONS && ctanns != null) {
+ out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
+ ctanns.put(out);
+ }
+ if (ClassReader.ANNOTATIONS && ictanns != null) {
+ out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
+ ictanns.put(out);
+ }
if (cattrs != null) {
cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out);
}
@@ -2095,6 +2351,12 @@ final void put(final ByteVector out) {
out.putShort(cw.newUTF8("Signature")).putInt(2)
.putShort(cw.newUTF8(signature));
}
+ if (methodParameters != null) {
+ out.putShort(cw.newUTF8("MethodParameters"));
+ out.putInt(methodParameters.length + 1).putByte(
+ methodParametersCount);
+ out.putByteArray(methodParameters.data, 0, methodParameters.length);
+ }
if (ClassReader.ANNOTATIONS && annd != null) {
out.putShort(cw.newUTF8("AnnotationDefault"));
out.putInt(annd.length);
@@ -2108,6 +2370,14 @@ final void put(final ByteVector out) {
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out);
}
+ if (ClassReader.ANNOTATIONS && tanns != null) {
+ out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
+ tanns.put(out);
+ }
+ if (ClassReader.ANNOTATIONS && itanns != null) {
+ out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
+ itanns.put(out);
+ }
if (ClassReader.ANNOTATIONS && panns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
AnnotationWriter.put(panns, synthetics, out);
@@ -2120,566 +2390,4 @@ final void put(final ByteVector out) {
attrs.put(cw, null, 0, -1, -1, out);
}
}
-
- // ------------------------------------------------------------------------
- // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
- // ------------------------------------------------------------------------
-
- /**
- * Resizes and replaces the temporary instructions inserted by
- * {@link Label#resolve} for wide forward jumps, while keeping jump offsets
- * and instruction addresses consistent. This may require to resize other
- * existing instructions, or even to introduce new instructions: for
- * example, increasing the size of an instruction by 2 at the middle of a
- * method can increases the offset of an IFEQ instruction from 32766 to
- * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W
- * 32765. This, in turn, may require to increase the size of another jump
- * instruction, and so on... All these operations are handled automatically
- * by this method.
- *
- * This method must be called after all the method that is being built
- * has been visited. In particular, the {@link Label Label} objects used
- * to construct the method are no longer valid after this method has been
- * called.
- */
- private void resizeInstructions() {
- byte[] b = code.data; // bytecode of the method
- int u, v, label; // indexes in b
- int i, j; // loop indexes
- /*
- * 1st step: As explained above, resizing an instruction may require to
- * resize another one, which may require to resize yet another one, and
- * so on. The first step of the algorithm consists in finding all the
- * instructions that need to be resized, without modifying the code.
- * This is done by the following "fix point" algorithm:
- *
- * Parse the code to find the jump instructions whose offset will need
- * more than 2 bytes to be stored (the future offset is computed from
- * the current offset and from the number of bytes that will be inserted
- * or removed between the source and target instructions). For each such
- * instruction, adds an entry in (a copy of) the indexes and sizes
- * arrays (if this has not already been done in a previous iteration!).
- *
- * If at least one entry has been added during the previous step, go
- * back to the beginning, otherwise stop.
- *
- * In fact the real algorithm is complicated by the fact that the size
- * of TABLESWITCH and LOOKUPSWITCH instructions depends on their
- * position in the bytecode (because of padding). In order to ensure the
- * convergence of the algorithm, the number of bytes to be added or
- * removed from these instructions is over estimated during the previous
- * loop, and computed exactly only after the loop is finished (this
- * requires another pass to parse the bytecode of the method).
- */
- int[] allIndexes = new int[0]; // copy of indexes
- int[] allSizes = new int[0]; // copy of sizes
- boolean[] resize; // instructions to be resized
- int newOffset; // future offset of a jump instruction
-
- resize = new boolean[code.length];
-
- // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
- int state = 3;
- do {
- if (state == 3) {
- state = 2;
- }
- u = 0;
- while (u < b.length) {
- int opcode = b[u] & 0xFF; // opcode of current instruction
- int insert = 0; // bytes to be added after this instruction
-
- switch (ClassWriter.TYPE[opcode]) {
- case ClassWriter.NOARG_INSN:
- case ClassWriter.IMPLVAR_INSN:
- u += 1;
- break;
- case ClassWriter.LABEL_INSN:
- if (opcode > 201) {
- // converts temporary opcodes 202 to 217, 218 and
- // 219 to IFEQ ... JSR (inclusive), IFNULL and
- // IFNONNULL
- opcode = opcode < 218 ? opcode - 49 : opcode - 20;
- label = u + readUnsignedShort(b, u + 1);
- } else {
- label = u + readShort(b, u + 1);
- }
- newOffset = getNewOffset(allIndexes, allSizes, u, label);
- if (newOffset < Short.MIN_VALUE
- || newOffset > Short.MAX_VALUE) {
- if (!resize[u]) {
- if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
- // two additional bytes will be required to
- // replace this GOTO or JSR instruction with
- // a GOTO_W or a JSR_W
- insert = 2;
- } else {
- // five additional bytes will be required to
- // replace this IFxxx instruction with
- // IFNOTxxx GOTO_W , where IFNOTxxx
- // is the "opposite" opcode of IFxxx (i.e.,
- // IFNE for IFEQ) and where designates
- // the instruction just after the GOTO_W.
- insert = 5;
- }
- resize[u] = true;
- }
- }
- u += 3;
- break;
- case ClassWriter.LABELW_INSN:
- u += 5;
- break;
- case ClassWriter.TABL_INSN:
- if (state == 1) {
- // true number of bytes to be added (or removed)
- // from this instruction = (future number of padding
- // bytes - current number of padding byte) -
- // previously over estimated variation =
- // = ((3 - newOffset%4) - (3 - u%4)) - u%4
- // = (-newOffset%4 + u%4) - u%4
- // = -(newOffset & 3)
- newOffset = getNewOffset(allIndexes, allSizes, 0, u);
- insert = -(newOffset & 3);
- } else if (!resize[u]) {
- // over estimation of the number of bytes to be
- // added to this instruction = 3 - current number
- // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3
- insert = u & 3;
- resize[u] = true;
- }
- // skips instruction
- u = u + 4 - (u & 3);
- u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;
- break;
- case ClassWriter.LOOK_INSN:
- if (state == 1) {
- // like TABL_INSN
- newOffset = getNewOffset(allIndexes, allSizes, 0, u);
- insert = -(newOffset & 3);
- } else if (!resize[u]) {
- // like TABL_INSN
- insert = u & 3;
- resize[u] = true;
- }
- // skips instruction
- u = u + 4 - (u & 3);
- u += 8 * readInt(b, u + 4) + 8;
- break;
- case ClassWriter.WIDE_INSN:
- opcode = b[u + 1] & 0xFF;
- if (opcode == Opcodes.IINC) {
- u += 6;
- } else {
- u += 4;
- }
- break;
- case ClassWriter.VAR_INSN:
- case ClassWriter.SBYTE_INSN:
- case ClassWriter.LDC_INSN:
- u += 2;
- break;
- case ClassWriter.SHORT_INSN:
- case ClassWriter.LDCW_INSN:
- case ClassWriter.FIELDORMETH_INSN:
- case ClassWriter.TYPE_INSN:
- case ClassWriter.IINC_INSN:
- u += 3;
- break;
- case ClassWriter.ITFMETH_INSN:
- case ClassWriter.INDYMETH_INSN:
- u += 5;
- break;
- // case ClassWriter.MANA_INSN:
- default:
- u += 4;
- break;
- }
- if (insert != 0) {
- // adds a new (u, insert) entry in the allIndexes and
- // allSizes arrays
- int[] newIndexes = new int[allIndexes.length + 1];
- int[] newSizes = new int[allSizes.length + 1];
- System.arraycopy(allIndexes, 0, newIndexes, 0,
- allIndexes.length);
- System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length);
- newIndexes[allIndexes.length] = u;
- newSizes[allSizes.length] = insert;
- allIndexes = newIndexes;
- allSizes = newSizes;
- if (insert > 0) {
- state = 3;
- }
- }
- }
- if (state < 3) {
- --state;
- }
- } while (state != 0);
-
- // 2nd step:
- // copies the bytecode of the method into a new bytevector, updates the
- // offsets, and inserts (or removes) bytes as requested.
-
- ByteVector newCode = new ByteVector(code.length);
-
- u = 0;
- while (u < code.length) {
- int opcode = b[u] & 0xFF;
- switch (ClassWriter.TYPE[opcode]) {
- case ClassWriter.NOARG_INSN:
- case ClassWriter.IMPLVAR_INSN:
- newCode.putByte(opcode);
- u += 1;
- break;
- case ClassWriter.LABEL_INSN:
- if (opcode > 201) {
- // changes temporary opcodes 202 to 217 (inclusive), 218
- // and 219 to IFEQ ... JSR (inclusive), IFNULL and
- // IFNONNULL
- opcode = opcode < 218 ? opcode - 49 : opcode - 20;
- label = u + readUnsignedShort(b, u + 1);
- } else {
- label = u + readShort(b, u + 1);
- }
- newOffset = getNewOffset(allIndexes, allSizes, u, label);
- if (resize[u]) {
- // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
- // with IFNOTxxx GOTO_W , where IFNOTxxx is
- // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
- // and where designates the instruction just after
- // the GOTO_W.
- if (opcode == Opcodes.GOTO) {
- newCode.putByte(200); // GOTO_W
- } else if (opcode == Opcodes.JSR) {
- newCode.putByte(201); // JSR_W
- } else {
- newCode.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
- : opcode ^ 1);
- newCode.putShort(8); // jump offset
- newCode.putByte(200); // GOTO_W
- // newOffset now computed from start of GOTO_W
- newOffset -= 3;
- }
- newCode.putInt(newOffset);
- } else {
- newCode.putByte(opcode);
- newCode.putShort(newOffset);
- }
- u += 3;
- break;
- case ClassWriter.LABELW_INSN:
- label = u + readInt(b, u + 1);
- newOffset = getNewOffset(allIndexes, allSizes, u, label);
- newCode.putByte(opcode);
- newCode.putInt(newOffset);
- u += 5;
- break;
- case ClassWriter.TABL_INSN:
- // skips 0 to 3 padding bytes
- v = u;
- u = u + 4 - (v & 3);
- // reads and copies instruction
- newCode.putByte(Opcodes.TABLESWITCH);
- newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4);
- label = v + readInt(b, u);
- u += 4;
- newOffset = getNewOffset(allIndexes, allSizes, v, label);
- newCode.putInt(newOffset);
- j = readInt(b, u);
- u += 4;
- newCode.putInt(j);
- j = readInt(b, u) - j + 1;
- u += 4;
- newCode.putInt(readInt(b, u - 4));
- for (; j > 0; --j) {
- label = v + readInt(b, u);
- u += 4;
- newOffset = getNewOffset(allIndexes, allSizes, v, label);
- newCode.putInt(newOffset);
- }
- break;
- case ClassWriter.LOOK_INSN:
- // skips 0 to 3 padding bytes
- v = u;
- u = u + 4 - (v & 3);
- // reads and copies instruction
- newCode.putByte(Opcodes.LOOKUPSWITCH);
- newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4);
- label = v + readInt(b, u);
- u += 4;
- newOffset = getNewOffset(allIndexes, allSizes, v, label);
- newCode.putInt(newOffset);
- j = readInt(b, u);
- u += 4;
- newCode.putInt(j);
- for (; j > 0; --j) {
- newCode.putInt(readInt(b, u));
- u += 4;
- label = v + readInt(b, u);
- u += 4;
- newOffset = getNewOffset(allIndexes, allSizes, v, label);
- newCode.putInt(newOffset);
- }
- break;
- case ClassWriter.WIDE_INSN:
- opcode = b[u + 1] & 0xFF;
- if (opcode == Opcodes.IINC) {
- newCode.putByteArray(b, u, 6);
- u += 6;
- } else {
- newCode.putByteArray(b, u, 4);
- u += 4;
- }
- break;
- case ClassWriter.VAR_INSN:
- case ClassWriter.SBYTE_INSN:
- case ClassWriter.LDC_INSN:
- newCode.putByteArray(b, u, 2);
- u += 2;
- break;
- case ClassWriter.SHORT_INSN:
- case ClassWriter.LDCW_INSN:
- case ClassWriter.FIELDORMETH_INSN:
- case ClassWriter.TYPE_INSN:
- case ClassWriter.IINC_INSN:
- newCode.putByteArray(b, u, 3);
- u += 3;
- break;
- case ClassWriter.ITFMETH_INSN:
- case ClassWriter.INDYMETH_INSN:
- newCode.putByteArray(b, u, 5);
- u += 5;
- break;
- // case MANA_INSN:
- default:
- newCode.putByteArray(b, u, 4);
- u += 4;
- break;
- }
- }
-
- // recomputes the stack map frames
- if (frameCount > 0) {
- if (compute == FRAMES) {
- frameCount = 0;
- stackMap = null;
- previousFrame = null;
- frame = null;
- Frame f = new Frame();
- f.owner = labels;
- Type[] args = Type.getArgumentTypes(descriptor);
- f.initInputFrame(cw, access, args, maxLocals);
- visitFrame(f);
- Label l = labels;
- while (l != null) {
- /*
- * here we need the original label position. getNewOffset
- * must therefore never have been called for this label.
- */
- u = l.position - 3;
- if ((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) {
- getNewOffset(allIndexes, allSizes, l);
- // TODO update offsets in UNINITIALIZED values
- visitFrame(l.frame);
- }
- l = l.successor;
- }
- } else {
- /*
- * Resizing an existing stack map frame table is really hard.
- * Not only the table must be parsed to update the offets, but
- * new frames may be needed for jump instructions that were
- * inserted by this method. And updating the offsets or
- * inserting frames can change the format of the following
- * frames, in case of packed frames. In practice the whole table
- * must be recomputed. For this the frames are marked as
- * potentially invalid. This will cause the whole class to be
- * reread and rewritten with the COMPUTE_FRAMES option (see the
- * ClassWriter.toByteArray method). This is not very efficient
- * but is much easier and requires much less code than any other
- * method I can think of.
- */
- cw.invalidFrames = true;
- }
- }
- // updates the exception handler block labels
- Handler h = firstHandler;
- while (h != null) {
- getNewOffset(allIndexes, allSizes, h.start);
- getNewOffset(allIndexes, allSizes, h.end);
- getNewOffset(allIndexes, allSizes, h.handler);
- h = h.next;
- }
- // updates the instructions addresses in the
- // local var and line number tables
- for (i = 0; i < 2; ++i) {
- ByteVector bv = i == 0 ? localVar : localVarType;
- if (bv != null) {
- b = bv.data;
- u = 0;
- while (u < bv.length) {
- label = readUnsignedShort(b, u);
- newOffset = getNewOffset(allIndexes, allSizes, 0, label);
- writeShort(b, u, newOffset);
- label += readUnsignedShort(b, u + 2);
- newOffset = getNewOffset(allIndexes, allSizes, 0, label)
- - newOffset;
- writeShort(b, u + 2, newOffset);
- u += 10;
- }
- }
- }
- if (lineNumber != null) {
- b = lineNumber.data;
- u = 0;
- while (u < lineNumber.length) {
- writeShort(
- b,
- u,
- getNewOffset(allIndexes, allSizes, 0,
- readUnsignedShort(b, u)));
- u += 4;
- }
- }
- // updates the labels of the other attributes
- Attribute attr = cattrs;
- while (attr != null) {
- Label[] labels = attr.getLabels();
- if (labels != null) {
- for (i = labels.length - 1; i >= 0; --i) {
- getNewOffset(allIndexes, allSizes, labels[i]);
- }
- }
- attr = attr.next;
- }
-
- // replaces old bytecodes with new ones
- code = newCode;
- }
-
- /**
- * Reads an unsigned short value in the given byte array.
- *
- * @param b
- * a byte array.
- * @param index
- * the start index of the value to be read.
- * @return the read value.
- */
- static int readUnsignedShort(final byte[] b, final int index) {
- return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
- }
-
- /**
- * Reads a signed short value in the given byte array.
- *
- * @param b
- * a byte array.
- * @param index
- * the start index of the value to be read.
- * @return the read value.
- */
- static short readShort(final byte[] b, final int index) {
- return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
- }
-
- /**
- * Reads a signed int value in the given byte array.
- *
- * @param b
- * a byte array.
- * @param index
- * the start index of the value to be read.
- * @return the read value.
- */
- static int readInt(final byte[] b, final int index) {
- return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
- | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
- }
-
- /**
- * Writes a short value in the given byte array.
- *
- * @param b
- * a byte array.
- * @param index
- * where the first byte of the short value must be written.
- * @param s
- * the value to be written in the given byte array.
- */
- static void writeShort(final byte[] b, final int index, final int s) {
- b[index] = (byte) (s >>> 8);
- b[index + 1] = (byte) s;
- }
-
- /**
- * Computes the future value of a bytecode offset.
- *
- * Note: it is possible to have several entries for the same instruction in
- * the indexes and sizes: two entries (index=a,size=b) and
- * (index=a,size=b') are equivalent to a single entry (index=a,size=b+b').
- *
- * @param indexes
- * current positions of the instructions to be resized. Each
- * instruction must be designated by the index of its last
- * byte, plus one (or, in other words, by the index of the
- * first byte of the next instruction).
- * @param sizes
- * the number of bytes to be added to the above
- * instructions. More precisely, for each i < len,
- * sizes[i] bytes will be added at the end of the
- * instruction designated by indexes[i] or, if
- * sizes[i] is negative, the last |
- * sizes[i]| bytes of the instruction will be removed
- * (the instruction size must not become negative or
- * null).
- * @param begin
- * index of the first byte of the source instruction.
- * @param end
- * index of the first byte of the target instruction.
- * @return the future value of the given bytecode offset.
- */
- static int getNewOffset(final int[] indexes, final int[] sizes,
- final int begin, final int end) {
- int offset = end - begin;
- for (int i = 0; i < indexes.length; ++i) {
- if (begin < indexes[i] && indexes[i] <= end) {
- // forward jump
- offset += sizes[i];
- } else if (end < indexes[i] && indexes[i] <= begin) {
- // backward jump
- offset -= sizes[i];
- }
- }
- return offset;
- }
-
- /**
- * Updates the offset of the given label.
- *
- * @param indexes
- * current positions of the instructions to be resized. Each
- * instruction must be designated by the index of its last
- * byte, plus one (or, in other words, by the index of the
- * first byte of the next instruction).
- * @param sizes
- * the number of bytes to be added to the above
- * instructions. More precisely, for each i < len,
- * sizes[i] bytes will be added at the end of the
- * instruction designated by indexes[i] or, if
- * sizes[i] is negative, the last |
- * sizes[i]| bytes of the instruction will be removed
- * (the instruction size must not become negative or
- * null).
- * @param label
- * the label whose offset must be updated.
- */
- static void getNewOffset(final int[] indexes, final int[] sizes,
- final Label label) {
- if ((label.status & Label.RESIZED) == 0) {
- label.position = getNewOffset(indexes, sizes, 0, label.position);
- label.status |= Label.RESIZED;
- }
- }
}
diff --git a/src/java/nginx/clojure/asm/Opcodes.java b/src/java/nginx/clojure/asm/Opcodes.java
index 656c1295..556a7a57 100644
--- a/src/java/nginx/clojure/asm/Opcodes.java
+++ b/src/java/nginx/clojure/asm/Opcodes.java
@@ -46,6 +46,7 @@ public interface Opcodes {
// ASM API versions
int ASM4 = 4 << 16 | 0 << 8 | 0;
+ int ASM5 = 5 << 16 | 0 << 8 | 0;
// versions
@@ -64,7 +65,7 @@ public interface Opcodes {
int ACC_PRIVATE = 0x0002; // class, field, method
int ACC_PROTECTED = 0x0004; // class, field, method
int ACC_STATIC = 0x0008; // field, method
- int ACC_FINAL = 0x0010; // class, field, method
+ int ACC_FINAL = 0x0010; // class, field, method, parameter
int ACC_SUPER = 0x0020; // class
int ACC_SYNCHRONIZED = 0x0020; // method
int ACC_VOLATILE = 0x0040; // field
@@ -75,9 +76,10 @@ public interface Opcodes {
int ACC_INTERFACE = 0x0200; // class
int ACC_ABSTRACT = 0x0400; // class, method
int ACC_STRICT = 0x0800; // method
- int ACC_SYNTHETIC = 0x1000; // class, field, method
+ int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter
int ACC_ANNOTATION = 0x2000; // class
int ACC_ENUM = 0x4000; // class(?) field inner
+ int ACC_MANDATED = 0x8000; // parameter
// ASM specific pseudo access flags
@@ -144,13 +146,17 @@ public interface Opcodes {
*/
int F_SAME1 = 4;
- Integer TOP = new Integer(0);
- Integer INTEGER = new Integer(1);
- Integer FLOAT = new Integer(2);
- Integer DOUBLE = new Integer(3);
- Integer LONG = new Integer(4);
- Integer NULL = new Integer(5);
- Integer UNINITIALIZED_THIS = new Integer(6);
+ // Do not try to change the following code to use auto-boxing,
+ // these values are compared by reference and not by value
+ // The constructor of Integer was deprecated in 9
+ // but we are stuck with it by backward compatibility
+ @SuppressWarnings("deprecation") Integer TOP = new Integer(0);
+ @SuppressWarnings("deprecation") Integer INTEGER = new Integer(1);
+ @SuppressWarnings("deprecation") Integer FLOAT = new Integer(2);
+ @SuppressWarnings("deprecation") Integer DOUBLE = new Integer(3);
+ @SuppressWarnings("deprecation") Integer LONG = new Integer(4);
+ @SuppressWarnings("deprecation") Integer NULL = new Integer(5);
+ @SuppressWarnings("deprecation") Integer UNINITIALIZED_THIS = new Integer(6);
// opcodes // visit method (- = idem)
diff --git a/src/java/nginx/clojure/asm/Type.java b/src/java/nginx/clojure/asm/Type.java
index 2ad1387e..602c22e3 100644
--- a/src/java/nginx/clojure/asm/Type.java
+++ b/src/java/nginx/clojure/asm/Type.java
@@ -377,7 +377,16 @@ public static Type[] getArgumentTypes(final Method method) {
*/
public static Type getReturnType(final String methodDescriptor) {
char[] buf = methodDescriptor.toCharArray();
- return getType(buf, methodDescriptor.indexOf(')') + 1);
+ int off = 1;
+ while (true) {
+ char car = buf[off++];
+ if (car == ')') {
+ return getType(buf, off);
+ } else if (car == 'L') {
+ while (buf[off++] != ';') {
+ }
+ }
+ }
}
/**
@@ -401,8 +410,8 @@ public static Type getReturnType(final Method method) {
* @return the size of the arguments of the method (plus one for the
* implicit this argument), argSize, and the size of its return
* value, retSize, packed into a single int i =
- * (argSize << 2) | retSize (argSize is therefore equal to
- * i >> 2, and retSize to i & 0x03).
+ * (argSize << 2) | retSize (argSize is therefore equal to
+ * i >> 2, and retSize to i & 0x03).
*/
public static int getArgumentsAndReturnSizes(final String desc) {
int n = 1;
@@ -556,11 +565,11 @@ public String getClassName() {
case DOUBLE:
return "double";
case ARRAY:
- StringBuffer b = new StringBuffer(getElementType().getClassName());
+ StringBuilder sb = new StringBuilder(getElementType().getClassName());
for (int i = getDimensions(); i > 0; --i) {
- b.append("[]");
+ sb.append("[]");
}
- return b.toString();
+ return sb.toString();
case OBJECT:
return new String(buf, off, len).replace('/', '.');
default:
@@ -606,9 +615,10 @@ public Type getReturnType() {
*
* @return the size of the arguments (plus one for the implicit this
* argument), argSize, and the size of the return value, retSize,
- * packed into a single int i = (argSize << 2) | retSize
- * (argSize is therefore equal to i >> 2, and retSize to
- * i & 0x03).
+ * packed into a single
+ * int i = (argSize << 2) | retSize
+ * (argSize is therefore equal to i >> 2,
+ * and retSize to i & 0x03).
*/
public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor());
@@ -624,7 +634,7 @@ public int getArgumentsAndReturnSizes() {
* @return the descriptor corresponding to this Java type.
*/
public String getDescriptor() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
getDescriptor(buf);
return buf.toString();
}
@@ -642,7 +652,7 @@ public String getDescriptor() {
*/
public static String getMethodDescriptor(final Type returnType,
final Type... argumentTypes) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append('(');
for (int i = 0; i < argumentTypes.length; ++i) {
argumentTypes[i].getDescriptor(buf);
@@ -659,7 +669,7 @@ public static String getMethodDescriptor(final Type returnType,
* @param buf
* the string buffer to which the descriptor must be appended.
*/
- private void getDescriptor(final StringBuffer buf) {
+ private void getDescriptor(final StringBuilder buf) {
if (this.buf == null) {
// descriptor is in byte 3 of 'off' for primitive types (buf ==
// null)
@@ -699,7 +709,7 @@ public static String getInternalName(final Class> c) {
* @return the descriptor corresponding to the given class.
*/
public static String getDescriptor(final Class> c) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
getDescriptor(buf, c);
return buf.toString();
}
@@ -713,7 +723,7 @@ public static String getDescriptor(final Class> c) {
*/
public static String getConstructorDescriptor(final Constructor> c) {
Class>[] parameters = c.getParameterTypes();
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append('(');
for (int i = 0; i < parameters.length; ++i) {
getDescriptor(buf, parameters[i]);
@@ -730,7 +740,7 @@ public static String getConstructorDescriptor(final Constructor> c) {
*/
public static String getMethodDescriptor(final Method m) {
Class>[] parameters = m.getParameterTypes();
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append('(');
for (int i = 0; i < parameters.length; ++i) {
getDescriptor(buf, parameters[i]);
@@ -748,7 +758,7 @@ public static String getMethodDescriptor(final Method m) {
* @param c
* the class whose descriptor must be computed.
*/
- private static void getDescriptor(final StringBuffer buf, final Class> c) {
+ private static void getDescriptor(final StringBuilder buf, final Class> c) {
Class> d = c;
while (true) {
if (d.isPrimitive()) {
diff --git a/src/java/nginx/clojure/asm/TypePath.java b/src/java/nginx/clojure/asm/TypePath.java
new file mode 100644
index 00000000..a553b594
--- /dev/null
+++ b/src/java/nginx/clojure/asm/TypePath.java
@@ -0,0 +1,196 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2013 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package nginx.clojure.asm;
+
+/**
+ * The path to a type argument, wildcard bound, array element type, or static
+ * inner type within an enclosing type.
+ *
+ * @author Eric Bruneton
+ */
+public class TypePath {
+
+ /**
+ * A type path step that steps into the element type of an array type. See
+ * {@link #getStep getStep}.
+ */
+ public final static int ARRAY_ELEMENT = 0;
+
+ /**
+ * A type path step that steps into the nested type of a class type. See
+ * {@link #getStep getStep}.
+ */
+ public final static int INNER_TYPE = 1;
+
+ /**
+ * A type path step that steps into the bound of a wildcard type. See
+ * {@link #getStep getStep}.
+ */
+ public final static int WILDCARD_BOUND = 2;
+
+ /**
+ * A type path step that steps into a type argument of a generic type. See
+ * {@link #getStep getStep}.
+ */
+ public final static int TYPE_ARGUMENT = 3;
+
+ /**
+ * The byte array where the path is stored, in Java class file format.
+ */
+ byte[] b;
+
+ /**
+ * The offset of the first byte of the type path in 'b'.
+ */
+ int offset;
+
+ /**
+ * Creates a new type path.
+ *
+ * @param b
+ * the byte array containing the type path in Java class file
+ * format.
+ * @param offset
+ * the offset of the first byte of the type path in 'b'.
+ */
+ TypePath(byte[] b, int offset) {
+ this.b = b;
+ this.offset = offset;
+ }
+
+ /**
+ * Returns the length of this path.
+ *
+ * @return the length of this path.
+ */
+ public int getLength() {
+ return b[offset];
+ }
+
+ /**
+ * Returns the value of the given step of this path.
+ *
+ * @param index
+ * an index between 0 and {@link #getLength()}, exclusive.
+ * @return {@link #ARRAY_ELEMENT ARRAY_ELEMENT}, {@link #INNER_TYPE
+ * INNER_TYPE}, {@link #WILDCARD_BOUND WILDCARD_BOUND}, or
+ * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}.
+ */
+ public int getStep(int index) {
+ return b[offset + 2 * index + 1];
+ }
+
+ /**
+ * Returns the index of the type argument that the given step is stepping
+ * into. This method should only be used for steps whose value is
+ * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}.
+ *
+ * @param index
+ * an index between 0 and {@link #getLength()}, exclusive.
+ * @return the index of the type argument that the given step is stepping
+ * into.
+ */
+ public int getStepArgument(int index) {
+ return b[offset + 2 * index + 2];
+ }
+
+ /**
+ * Converts a type path in string form, in the format used by
+ * {@link #toString()}, into a TypePath object.
+ *
+ * @param typePath
+ * a type path in string form, in the format used by
+ * {@link #toString()}. May be null or empty.
+ * @return the corresponding TypePath object, or null if the path is empty.
+ */
+ public static TypePath fromString(final String typePath) {
+ if (typePath == null || typePath.length() == 0) {
+ return null;
+ }
+ int n = typePath.length();
+ ByteVector out = new ByteVector(n);
+ out.putByte(0);
+ for (int i = 0; i < n;) {
+ char c = typePath.charAt(i++);
+ if (c == '[') {
+ out.put11(ARRAY_ELEMENT, 0);
+ } else if (c == '.') {
+ out.put11(INNER_TYPE, 0);
+ } else if (c == '*') {
+ out.put11(WILDCARD_BOUND, 0);
+ } else if (c >= '0' && c <= '9') {
+ int typeArg = c - '0';
+ while (i < n && (c = typePath.charAt(i)) >= '0' && c <= '9') {
+ typeArg = typeArg * 10 + c - '0';
+ i += 1;
+ }
+ if (i < n && typePath.charAt(i) == ';') {
+ i += 1;
+ }
+ out.put11(TYPE_ARGUMENT, typeArg);
+ }
+ }
+ out.data[0] = (byte) (out.length / 2);
+ return new TypePath(out.data, 0);
+ }
+
+ /**
+ * Returns a string representation of this type path. {@link #ARRAY_ELEMENT
+ * ARRAY_ELEMENT} steps are represented with '[', {@link #INNER_TYPE
+ * INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND WILDCARD_BOUND} steps
+ * with '*' and {@link #TYPE_ARGUMENT TYPE_ARGUMENT} steps with their type
+ * argument index in decimal form followed by ';'.
+ */
+ @Override
+ public String toString() {
+ int length = getLength();
+ StringBuilder result = new StringBuilder(length * 2);
+ for (int i = 0; i < length; ++i) {
+ switch (getStep(i)) {
+ case ARRAY_ELEMENT:
+ result.append('[');
+ break;
+ case INNER_TYPE:
+ result.append('.');
+ break;
+ case WILDCARD_BOUND:
+ result.append('*');
+ break;
+ case TYPE_ARGUMENT:
+ result.append(getStepArgument(i)).append(';');
+ break;
+ default:
+ result.append('_');
+ }
+ }
+ return result.toString();
+ }
+}
diff --git a/src/java/nginx/clojure/asm/TypeReference.java b/src/java/nginx/clojure/asm/TypeReference.java
new file mode 100644
index 00000000..7a3be791
--- /dev/null
+++ b/src/java/nginx/clojure/asm/TypeReference.java
@@ -0,0 +1,452 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2013 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package nginx.clojure.asm;
+
+/**
+ * A reference to a type appearing in a class, field or method declaration, or
+ * on an instruction. Such a reference designates the part of the class where
+ * the referenced type is appearing (e.g. an 'extends', 'implements' or 'throws'
+ * clause, a 'new' instruction, a 'catch' clause, a type cast, a local variable
+ * declaration, etc).
+ *
+ * @author Eric Bruneton
+ */
+public class TypeReference {
+
+ /**
+ * The sort of type references that target a type parameter of a generic
+ * class. See {@link #getSort getSort}.
+ */
+ public final static int CLASS_TYPE_PARAMETER = 0x00;
+
+ /**
+ * The sort of type references that target a type parameter of a generic
+ * method. See {@link #getSort getSort}.
+ */
+ public final static int METHOD_TYPE_PARAMETER = 0x01;
+
+ /**
+ * The sort of type references that target the super class of a class or one
+ * of the interfaces it implements. See {@link #getSort getSort}.
+ */
+ public final static int CLASS_EXTENDS = 0x10;
+
+ /**
+ * The sort of type references that target a bound of a type parameter of a
+ * generic class. See {@link #getSort getSort}.
+ */
+ public final static int CLASS_TYPE_PARAMETER_BOUND = 0x11;
+
+ /**
+ * The sort of type references that target a bound of a type parameter of a
+ * generic method. See {@link #getSort getSort}.
+ */
+ public final static int METHOD_TYPE_PARAMETER_BOUND = 0x12;
+
+ /**
+ * The sort of type references that target the type of a field. See
+ * {@link #getSort getSort}.
+ */
+ public final static int FIELD = 0x13;
+
+ /**
+ * The sort of type references that target the return type of a method. See
+ * {@link #getSort getSort}.
+ */
+ public final static int METHOD_RETURN = 0x14;
+
+ /**
+ * The sort of type references that target the receiver type of a method.
+ * See {@link #getSort getSort}.
+ */
+ public final static int METHOD_RECEIVER = 0x15;
+
+ /**
+ * The sort of type references that target the type of a formal parameter of
+ * a method. See {@link #getSort getSort}.
+ */
+ public final static int METHOD_FORMAL_PARAMETER = 0x16;
+
+ /**
+ * The sort of type references that target the type of an exception declared
+ * in the throws clause of a method. See {@link #getSort getSort}.
+ */
+ public final static int THROWS = 0x17;
+
+ /**
+ * The sort of type references that target the type of a local variable in a
+ * method. See {@link #getSort getSort}.
+ */
+ public final static int LOCAL_VARIABLE = 0x40;
+
+ /**
+ * The sort of type references that target the type of a resource variable
+ * in a method. See {@link #getSort getSort}.
+ */
+ public final static int RESOURCE_VARIABLE = 0x41;
+
+ /**
+ * The sort of type references that target the type of the exception of a
+ * 'catch' clause in a method. See {@link #getSort getSort}.
+ */
+ public final static int EXCEPTION_PARAMETER = 0x42;
+
+ /**
+ * The sort of type references that target the type declared in an
+ * 'instanceof' instruction. See {@link #getSort getSort}.
+ */
+ public final static int INSTANCEOF = 0x43;
+
+ /**
+ * The sort of type references that target the type of the object created by
+ * a 'new' instruction. See {@link #getSort getSort}.
+ */
+ public final static int NEW = 0x44;
+
+ /**
+ * The sort of type references that target the receiver type of a
+ * constructor reference. See {@link #getSort getSort}.
+ */
+ public final static int CONSTRUCTOR_REFERENCE = 0x45;
+
+ /**
+ * The sort of type references that target the receiver type of a method
+ * reference. See {@link #getSort getSort}.
+ */
+ public final static int METHOD_REFERENCE = 0x46;
+
+ /**
+ * The sort of type references that target the type declared in an explicit
+ * or implicit cast instruction. See {@link #getSort getSort}.
+ */
+ public final static int CAST = 0x47;
+
+ /**
+ * The sort of type references that target a type parameter of a generic
+ * constructor in a constructor call. See {@link #getSort getSort}.
+ */
+ public final static int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48;
+
+ /**
+ * The sort of type references that target a type parameter of a generic
+ * method in a method call. See {@link #getSort getSort}.
+ */
+ public final static int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49;
+
+ /**
+ * The sort of type references that target a type parameter of a generic
+ * constructor in a constructor reference. See {@link #getSort getSort}.
+ */
+ public final static int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A;
+
+ /**
+ * The sort of type references that target a type parameter of a generic
+ * method in a method reference. See {@link #getSort getSort}.
+ */
+ public final static int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B;
+
+ /**
+ * The type reference value in Java class file format.
+ */
+ private int value;
+
+ /**
+ * Creates a new TypeReference.
+ *
+ * @param typeRef
+ * the int encoded value of the type reference, as received in a
+ * visit method related to type annotations, like
+ * visitTypeAnnotation.
+ */
+ public TypeReference(int typeRef) {
+ this.value = typeRef;
+ }
+
+ /**
+ * Returns a type reference of the given sort.
+ *
+ * @param sort
+ * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN},
+ * {@link #METHOD_RECEIVER METHOD_RECEIVER},
+ * {@link #LOCAL_VARIABLE LOCAL_VARIABLE},
+ * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE},
+ * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW},
+ * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, or
+ * {@link #METHOD_REFERENCE METHOD_REFERENCE}.
+ * @return a type reference of the given sort.
+ */
+ public static TypeReference newTypeReference(int sort) {
+ return new TypeReference(sort << 24);
+ }
+
+ /**
+ * Returns a reference to a type parameter of a generic class or method.
+ *
+ * @param sort
+ * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or
+ * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}.
+ * @param paramIndex
+ * the type parameter index.
+ * @return a reference to the given generic class or method type parameter.
+ */
+ public static TypeReference newTypeParameterReference(int sort,
+ int paramIndex) {
+ return new TypeReference((sort << 24) | (paramIndex << 16));
+ }
+
+ /**
+ * Returns a reference to a type parameter bound of a generic class or
+ * method.
+ *
+ * @param sort
+ * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or
+ * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}.
+ * @param paramIndex
+ * the type parameter index.
+ * @param boundIndex
+ * the type bound index within the above type parameters.
+ * @return a reference to the given generic class or method type parameter
+ * bound.
+ */
+ public static TypeReference newTypeParameterBoundReference(int sort,
+ int paramIndex, int boundIndex) {
+ return new TypeReference((sort << 24) | (paramIndex << 16)
+ | (boundIndex << 8));
+ }
+
+ /**
+ * Returns a reference to the super class or to an interface of the
+ * 'implements' clause of a class.
+ *
+ * @param itfIndex
+ * the index of an interface in the 'implements' clause of a
+ * class, or -1 to reference the super class of the class.
+ * @return a reference to the given super type of a class.
+ */
+ public static TypeReference newSuperTypeReference(int itfIndex) {
+ itfIndex &= 0xFFFF;
+ return new TypeReference((CLASS_EXTENDS << 24) | (itfIndex << 8));
+ }
+
+ /**
+ * Returns a reference to the type of a formal parameter of a method.
+ *
+ * @param paramIndex
+ * the formal parameter index.
+ *
+ * @return a reference to the type of the given method formal parameter.
+ */
+ public static TypeReference newFormalParameterReference(int paramIndex) {
+ return new TypeReference((METHOD_FORMAL_PARAMETER << 24)
+ | (paramIndex << 16));
+ }
+
+ /**
+ * Returns a reference to the type of an exception, in a 'throws' clause of
+ * a method.
+ *
+ * @param exceptionIndex
+ * the index of an exception in a 'throws' clause of a method.
+ *
+ * @return a reference to the type of the given exception.
+ */
+ public static TypeReference newExceptionReference(int exceptionIndex) {
+ return new TypeReference((THROWS << 24) | (exceptionIndex << 8));
+ }
+
+ /**
+ * Returns a reference to the type of the exception declared in a 'catch'
+ * clause of a method.
+ *
+ * @param tryCatchBlockIndex
+ * the index of a try catch block (using the order in which they
+ * are visited with visitTryCatchBlock).
+ *
+ * @return a reference to the type of the given exception.
+ */
+ public static TypeReference newTryCatchReference(int tryCatchBlockIndex) {
+ return new TypeReference((EXCEPTION_PARAMETER << 24)
+ | (tryCatchBlockIndex << 8));
+ }
+
+ /**
+ * Returns a reference to the type of a type argument in a constructor or
+ * method call or reference.
+ *
+ * @param sort
+ * {@link #CAST CAST},
+ * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
+ * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
+ * {@link #METHOD_INVOCATION_TYPE_ARGUMENT
+ * METHOD_INVOCATION_TYPE_ARGUMENT},
+ * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
+ * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
+ * {@link #METHOD_REFERENCE_TYPE_ARGUMENT
+ * METHOD_REFERENCE_TYPE_ARGUMENT}.
+ * @param argIndex
+ * the type argument index.
+ *
+ * @return a reference to the type of the given type argument.
+ */
+ public static TypeReference newTypeArgumentReference(int sort, int argIndex) {
+ return new TypeReference((sort << 24) | argIndex);
+ }
+
+ /**
+ * Returns the sort of this type reference.
+ *
+ * @return {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER},
+ * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER},
+ * {@link #CLASS_EXTENDS CLASS_EXTENDS},
+ * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND},
+ * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND},
+ * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN},
+ * {@link #METHOD_RECEIVER METHOD_RECEIVER},
+ * {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER},
+ * {@link #THROWS THROWS}, {@link #LOCAL_VARIABLE LOCAL_VARIABLE},
+ * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE},
+ * {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER},
+ * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW},
+ * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE},
+ * {@link #METHOD_REFERENCE METHOD_REFERENCE}, {@link #CAST CAST},
+ * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
+ * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
+ * {@link #METHOD_INVOCATION_TYPE_ARGUMENT
+ * METHOD_INVOCATION_TYPE_ARGUMENT},
+ * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
+ * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
+ * {@link #METHOD_REFERENCE_TYPE_ARGUMENT
+ * METHOD_REFERENCE_TYPE_ARGUMENT}.
+ */
+ public int getSort() {
+ return value >>> 24;
+ }
+
+ /**
+ * Returns the index of the type parameter referenced by this type
+ * reference. This method must only be used for type references whose sort
+ * is {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER},
+ * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER},
+ * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or
+ * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}.
+ *
+ * @return a type parameter index.
+ */
+ public int getTypeParameterIndex() {
+ return (value & 0x00FF0000) >> 16;
+ }
+
+ /**
+ * Returns the index of the type parameter bound, within the type parameter
+ * {@link #getTypeParameterIndex}, referenced by this type reference. This
+ * method must only be used for type references whose sort is
+ * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or
+ * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}.
+ *
+ * @return a type parameter bound index.
+ */
+ public int getTypeParameterBoundIndex() {
+ return (value & 0x0000FF00) >> 8;
+ }
+
+ /**
+ * Returns the index of the "super type" of a class that is referenced by
+ * this type reference. This method must only be used for type references
+ * whose sort is {@link #CLASS_EXTENDS CLASS_EXTENDS}.
+ *
+ * @return the index of an interface in the 'implements' clause of a class,
+ * or -1 if this type reference references the type of the super
+ * class.
+ */
+ public int getSuperTypeIndex() {
+ return (short) ((value & 0x00FFFF00) >> 8);
+ }
+
+ /**
+ * Returns the index of the formal parameter whose type is referenced by
+ * this type reference. This method must only be used for type references
+ * whose sort is {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}.
+ *
+ * @return a formal parameter index.
+ */
+ public int getFormalParameterIndex() {
+ return (value & 0x00FF0000) >> 16;
+ }
+
+ /**
+ * Returns the index of the exception, in a 'throws' clause of a method,
+ * whose type is referenced by this type reference. This method must only be
+ * used for type references whose sort is {@link #THROWS THROWS}.
+ *
+ * @return the index of an exception in the 'throws' clause of a method.
+ */
+ public int getExceptionIndex() {
+ return (value & 0x00FFFF00) >> 8;
+ }
+
+ /**
+ * Returns the index of the try catch block (using the order in which they
+ * are visited with visitTryCatchBlock), whose 'catch' type is referenced by
+ * this type reference. This method must only be used for type references
+ * whose sort is {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER} .
+ *
+ * @return the index of an exception in the 'throws' clause of a method.
+ */
+ public int getTryCatchBlockIndex() {
+ return (value & 0x00FFFF00) >> 8;
+ }
+
+ /**
+ * Returns the index of the type argument referenced by this type reference.
+ * This method must only be used for type references whose sort is
+ * {@link #CAST CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
+ * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
+ * {@link #METHOD_INVOCATION_TYPE_ARGUMENT METHOD_INVOCATION_TYPE_ARGUMENT},
+ * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
+ * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
+ * {@link #METHOD_REFERENCE_TYPE_ARGUMENT METHOD_REFERENCE_TYPE_ARGUMENT}.
+ *
+ * @return a type parameter index.
+ */
+ public int getTypeArgumentIndex() {
+ return value & 0xFF;
+ }
+
+ /**
+ * Returns the int encoded value of this type reference, suitable for use in
+ * visit methods related to type annotations, like visitTypeAnnotation.
+ *
+ * @return the int encoded value of this type reference.
+ */
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/src/java/nginx/clojure/asm/commons/AdviceAdapter.java b/src/java/nginx/clojure/asm/commons/AdviceAdapter.java
index 9e983dc8..f11e2631 100644
--- a/src/java/nginx/clojure/asm/commons/AdviceAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/AdviceAdapter.java
@@ -83,7 +83,7 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param mv
* the method visitor to which this adapter delegates calls.
* @param access
@@ -359,10 +359,10 @@ public void visitFieldInsn(final int opcode, final String owner,
}
break;
case PUTFIELD:
+ popValue();
popValue();
if (longOrDouble) {
popValue();
- popValue();
}
break;
// case GETFIELD:
@@ -413,10 +413,31 @@ public void visitTypeInsn(final int opcode, final String type) {
}
}
+ @Deprecated
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
- mv.visitMethodInsn(opcode, owner, name, desc);
+ if (api >= Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc,
+ opcode == Opcodes.INVOKEINTERFACE);
+ }
+
+ @Override
+ public void visitMethodInsn(final int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
+ if (api < Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ private void doVisitMethodInsn(int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
+ mv.visitMethodInsn(opcode, owner, name, desc, itf);
if (constructor) {
Type[] types = Type.getArgumentTypes(desc);
for (int i = 0; i < types.length; i++) {
@@ -569,7 +590,7 @@ private void pushValue(final Object o) {
}
/**
- * Called at the beginning of the method or after super class class call in
+ * Called at the beginning of the method or after super class call in
* the constructor.
*
*
diff --git a/src/java/nginx/clojure/asm/commons/AnalyzerAdapter.java b/src/java/nginx/clojure/asm/commons/AnalyzerAdapter.java
index 1371a248..656718e0 100644
--- a/src/java/nginx/clojure/asm/commons/AnalyzerAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/AnalyzerAdapter.java
@@ -136,10 +136,15 @@ public class AnalyzerAdapter extends MethodVisitor {
* @param mv
* the method visitor to which this adapter delegates calls. May
* be null.
+ * @throws IllegalStateException
+ * If a subclass calls this constructor.
*/
public AnalyzerAdapter(final String owner, final int access,
final String name, final String desc, final MethodVisitor mv) {
- this(Opcodes.ASM4, owner, access, name, desc, mv);
+ this(Opcodes.ASM5, owner, access, name, desc, mv);
+ if (getClass() != AnalyzerAdapter.class) {
+ throw new IllegalStateException();
+ }
}
/**
@@ -147,7 +152,7 @@ public AnalyzerAdapter(final String owner, final int access,
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param owner
* the owner's class name.
* @param access
@@ -206,6 +211,7 @@ protected AnalyzerAdapter(final int api, final String owner,
locals.add(types[i].getInternalName());
}
}
+ maxLocals = locals.size();
}
@Override
@@ -302,11 +308,32 @@ public void visitFieldInsn(final int opcode, final String owner,
execute(opcode, 0, desc);
}
+ @Deprecated
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
+ if (api >= Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc,
+ opcode == Opcodes.INVOKEINTERFACE);
+ }
+
+ @Override
+ public void visitMethodInsn(final int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
+ if (api < Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ private void doVisitMethodInsn(int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
if (mv != null) {
- mv.visitMethodInsn(opcode, owner, name, desc);
+ mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
if (this.locals == null) {
labels = null;
@@ -464,12 +491,12 @@ public void visitMaxs(final int maxStack, final int maxLocals) {
// ------------------------------------------------------------------------
private Object get(final int local) {
- maxLocals = Math.max(maxLocals, local);
+ maxLocals = Math.max(maxLocals, local + 1);
return local < locals.size() ? locals.get(local) : Opcodes.TOP;
}
private void set(final int local, final Object type) {
- maxLocals = Math.max(maxLocals, local);
+ maxLocals = Math.max(maxLocals, local + 1);
while (local >= locals.size()) {
locals.add(Opcodes.TOP);
}
diff --git a/src/java/nginx/clojure/asm/commons/AnnotationRemapper.java b/src/java/nginx/clojure/asm/commons/AnnotationRemapper.java
new file mode 100644
index 00000000..b95e7f60
--- /dev/null
+++ b/src/java/nginx/clojure/asm/commons/AnnotationRemapper.java
@@ -0,0 +1,79 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package nginx.clojure.asm.commons;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.Opcodes;
+
+/**
+ * An {@link AnnotationVisitor} adapter for type remapping.
+ *
+ * @author Eugene Kuleshov
+ */
+public class AnnotationRemapper extends AnnotationVisitor {
+
+ protected final Remapper remapper;
+
+ public AnnotationRemapper(final AnnotationVisitor av,
+ final Remapper remapper) {
+ this(Opcodes.ASM5, av, remapper);
+ }
+
+ protected AnnotationRemapper(final int api, final AnnotationVisitor av,
+ final Remapper remapper) {
+ super(api, av);
+ this.remapper = remapper;
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ av.visit(name, remapper.mapValue(value));
+ }
+
+ @Override
+ public void visitEnum(String name, String desc, String value) {
+ av.visitEnum(name, remapper.mapDesc(desc), value);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ AnnotationVisitor v = av.visitAnnotation(name, remapper.mapDesc(desc));
+ return v == null ? null : (v == av ? this : new AnnotationRemapper(v,
+ remapper));
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ AnnotationVisitor v = av.visitArray(name);
+ return v == null ? null : (v == av ? this : new AnnotationRemapper(v,
+ remapper));
+ }
+}
diff --git a/src/java/nginx/clojure/asm/commons/ClassRemapper.java b/src/java/nginx/clojure/asm/commons/ClassRemapper.java
new file mode 100644
index 00000000..15f743af
--- /dev/null
+++ b/src/java/nginx/clojure/asm/commons/ClassRemapper.java
@@ -0,0 +1,132 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package nginx.clojure.asm.commons;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.ClassVisitor;
+import nginx.clojure.asm.FieldVisitor;
+import nginx.clojure.asm.MethodVisitor;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
+
+/**
+ * A {@link ClassVisitor} for type remapping.
+ *
+ * @author Eugene Kuleshov
+ */
+public class ClassRemapper extends ClassVisitor {
+
+ protected final Remapper remapper;
+
+ protected String className;
+
+ public ClassRemapper(final ClassVisitor cv, final Remapper remapper) {
+ this(Opcodes.ASM5, cv, remapper);
+ }
+
+ protected ClassRemapper(final int api, final ClassVisitor cv,
+ final Remapper remapper) {
+ super(api, cv);
+ this.remapper = remapper;
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature,
+ String superName, String[] interfaces) {
+ this.className = name;
+ super.visit(version, access, remapper.mapType(name), remapper
+ .mapSignature(signature, false), remapper.mapType(superName),
+ interfaces == null ? null : remapper.mapTypes(interfaces));
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ AnnotationVisitor av = super.visitAnnotation(remapper.mapDesc(desc),
+ visible);
+ return av == null ? null : createAnnotationRemapper(av);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
+ return av == null ? null : createAnnotationRemapper(av);
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc,
+ String signature, Object value) {
+ FieldVisitor fv = super.visitField(access,
+ remapper.mapFieldName(className, name, desc),
+ remapper.mapDesc(desc), remapper.mapSignature(signature, true),
+ remapper.mapValue(value));
+ return fv == null ? null : createFieldRemapper(fv);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ String newDesc = remapper.mapMethodDesc(desc);
+ MethodVisitor mv = super.visitMethod(access, remapper.mapMethodName(
+ className, name, desc), newDesc, remapper.mapSignature(
+ signature, false),
+ exceptions == null ? null : remapper.mapTypes(exceptions));
+ return mv == null ? null : createMethodRemapper(mv);
+ }
+
+ @Override
+ public void visitInnerClass(String name, String outerName,
+ String innerName, int access) {
+ // TODO should innerName be changed?
+ super.visitInnerClass(remapper.mapType(name), outerName == null ? null
+ : remapper.mapType(outerName), innerName, access);
+ }
+
+ @Override
+ public void visitOuterClass(String owner, String name, String desc) {
+ super.visitOuterClass(remapper.mapType(owner), name == null ? null
+ : remapper.mapMethodName(owner, name, desc),
+ desc == null ? null : remapper.mapMethodDesc(desc));
+ }
+
+ protected FieldVisitor createFieldRemapper(FieldVisitor fv) {
+ return new FieldRemapper(fv, remapper);
+ }
+
+ protected MethodVisitor createMethodRemapper(MethodVisitor mv) {
+ return new MethodRemapper(mv, remapper);
+ }
+
+ protected AnnotationVisitor createAnnotationRemapper(AnnotationVisitor av) {
+ return new AnnotationRemapper(av, remapper);
+ }
+}
diff --git a/src/java/nginx/clojure/asm/commons/CodeSizeEvaluator.java b/src/java/nginx/clojure/asm/commons/CodeSizeEvaluator.java
index 5454e60b..4ba0727c 100644
--- a/src/java/nginx/clojure/asm/commons/CodeSizeEvaluator.java
+++ b/src/java/nginx/clojure/asm/commons/CodeSizeEvaluator.java
@@ -46,7 +46,7 @@ public class CodeSizeEvaluator extends MethodVisitor implements Opcodes {
private int maxSize;
public CodeSizeEvaluator(final MethodVisitor mv) {
- this(Opcodes.ASM4, mv);
+ this(Opcodes.ASM5, mv);
}
protected CodeSizeEvaluator(final int api, final MethodVisitor mv) {
@@ -120,9 +120,30 @@ public void visitFieldInsn(final int opcode, final String owner,
}
}
+ @Deprecated
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
+ if (api >= Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc,
+ opcode == Opcodes.INVOKEINTERFACE);
+ }
+
+ @Override
+ public void visitMethodInsn(final int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
+ if (api < Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ private void doVisitMethodInsn(int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
if (opcode == INVOKEINTERFACE) {
minSize += 5;
maxSize += 5;
@@ -131,7 +152,7 @@ public void visitMethodInsn(final int opcode, final String owner,
maxSize += 3;
}
if (mv != null) {
- mv.visitMethodInsn(opcode, owner, name, desc);
+ mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
diff --git a/src/java/nginx/clojure/asm/commons/FieldRemapper.java b/src/java/nginx/clojure/asm/commons/FieldRemapper.java
new file mode 100644
index 00000000..c637cb43
--- /dev/null
+++ b/src/java/nginx/clojure/asm/commons/FieldRemapper.java
@@ -0,0 +1,71 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package nginx.clojure.asm.commons;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.FieldVisitor;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
+
+/**
+ * A {@link FieldVisitor} adapter for type remapping.
+ *
+ * @author Eugene Kuleshov
+ */
+public class FieldRemapper extends FieldVisitor {
+
+ private final Remapper remapper;
+
+ public FieldRemapper(final FieldVisitor fv, final Remapper remapper) {
+ this(Opcodes.ASM5, fv, remapper);
+ }
+
+ protected FieldRemapper(final int api, final FieldVisitor fv,
+ final Remapper remapper) {
+ super(api, fv);
+ this.remapper = remapper;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ AnnotationVisitor av = fv.visitAnnotation(remapper.mapDesc(desc),
+ visible);
+ return av == null ? null : new AnnotationRemapper(av, remapper);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
+ return av == null ? null : new AnnotationRemapper(av, remapper);
+ }
+}
diff --git a/src/java/nginx/clojure/asm/commons/GeneratorAdapter.java b/src/java/nginx/clojure/asm/commons/GeneratorAdapter.java
index 5aca835a..5ce30e7f 100644
--- a/src/java/nginx/clojure/asm/commons/GeneratorAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/GeneratorAdapter.java
@@ -255,10 +255,15 @@ public class GeneratorAdapter extends LocalVariablesSorter {
* the method's name.
* @param desc
* the method's descriptor (see {@link Type Type}).
+ * @throws IllegalStateException
+ * If a subclass calls this constructor.
*/
public GeneratorAdapter(final MethodVisitor mv, final int access,
final String name, final String desc) {
- this(Opcodes.ASM4, mv, access, name, desc);
+ this(Opcodes.ASM5, mv, access, name, desc);
+ if (getClass() != GeneratorAdapter.class) {
+ throw new IllegalStateException();
+ }
}
/**
@@ -266,7 +271,7 @@ public GeneratorAdapter(final MethodVisitor mv, final int access,
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param mv
* the method visitor to which this adapter delegates calls.
* @param access
@@ -374,7 +379,7 @@ public void push(final int value) {
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
- mv.visitLdcInsn(new Integer(value));
+ mv.visitLdcInsn(value);
}
}
@@ -388,7 +393,7 @@ public void push(final long value) {
if (value == 0L || value == 1L) {
mv.visitInsn(Opcodes.LCONST_0 + (int) value);
} else {
- mv.visitLdcInsn(new Long(value));
+ mv.visitLdcInsn(value);
}
}
@@ -403,7 +408,7 @@ public void push(final float value) {
if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
mv.visitInsn(Opcodes.FCONST_0 + (int) value);
} else {
- mv.visitLdcInsn(new Float(value));
+ mv.visitLdcInsn(value);
}
}
@@ -418,7 +423,7 @@ public void push(final double value) {
if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
mv.visitInsn(Opcodes.DCONST_0 + (int) value);
} else {
- mv.visitLdcInsn(new Double(value));
+ mv.visitLdcInsn(value);
}
}
@@ -1371,11 +1376,11 @@ public void putField(final Type owner, final String name, final Type type) {
* the method to be invoked.
*/
private void invokeInsn(final int opcode, final Type type,
- final Method method) {
+ final Method method, final boolean itf) {
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor()
: type.getInternalName();
mv.visitMethodInsn(opcode, owner, method.getName(),
- method.getDescriptor());
+ method.getDescriptor(), itf);
}
/**
@@ -1387,7 +1392,7 @@ private void invokeInsn(final int opcode, final Type type,
* the method to be invoked.
*/
public void invokeVirtual(final Type owner, final Method method) {
- invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
+ invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method, false);
}
/**
@@ -1399,7 +1404,7 @@ public void invokeVirtual(final Type owner, final Method method) {
* the constructor to be invoked.
*/
public void invokeConstructor(final Type type, final Method method) {
- invokeInsn(Opcodes.INVOKESPECIAL, type, method);
+ invokeInsn(Opcodes.INVOKESPECIAL, type, method, false);
}
/**
@@ -1411,7 +1416,7 @@ public void invokeConstructor(final Type type, final Method method) {
* the method to be invoked.
*/
public void invokeStatic(final Type owner, final Method method) {
- invokeInsn(Opcodes.INVOKESTATIC, owner, method);
+ invokeInsn(Opcodes.INVOKESTATIC, owner, method, false);
}
/**
@@ -1423,7 +1428,7 @@ public void invokeStatic(final Type owner, final Method method) {
* the method to be invoked.
*/
public void invokeInterface(final Type owner, final Method method) {
- invokeInsn(Opcodes.INVOKEINTERFACE, owner, method);
+ invokeInsn(Opcodes.INVOKEINTERFACE, owner, method, true);
}
/**
@@ -1613,11 +1618,13 @@ public void endMethod() {
*/
public void catchException(final Label start, final Label end,
final Type exception) {
+ Label doCatch = new Label();
if (exception == null) {
- mv.visitTryCatchBlock(start, end, mark(), null);
+ mv.visitTryCatchBlock(start, end, doCatch, null);
} else {
- mv.visitTryCatchBlock(start, end, mark(),
+ mv.visitTryCatchBlock(start, end, doCatch,
exception.getInternalName());
}
+ mark(doCatch);
}
}
diff --git a/src/java/nginx/clojure/asm/commons/InstructionAdapter.java b/src/java/nginx/clojure/asm/commons/InstructionAdapter.java
index 4810ed09..f660481a 100644
--- a/src/java/nginx/clojure/asm/commons/InstructionAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/InstructionAdapter.java
@@ -53,9 +53,14 @@ public class InstructionAdapter extends MethodVisitor {
*
* @param mv
* the method visitor to which this adapter delegates calls.
+ * @throws IllegalStateException
+ * If a subclass calls this constructor.
*/
public InstructionAdapter(final MethodVisitor mv) {
- this(Opcodes.ASM4, mv);
+ this(Opcodes.ASM5, mv);
+ if (getClass() != InstructionAdapter.class) {
+ throw new IllegalStateException();
+ }
}
/**
@@ -63,7 +68,7 @@ public InstructionAdapter(final MethodVisitor mv) {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param mv
* the method visitor to which this adapter delegates calls.
*/
@@ -507,18 +512,39 @@ public void visitFieldInsn(final int opcode, final String owner,
}
}
+ @Deprecated
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
+ if (api >= Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc,
+ opcode == Opcodes.INVOKEINTERFACE);
+ }
+
+ @Override
+ public void visitMethodInsn(final int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
+ if (api < Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ private void doVisitMethodInsn(int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
switch (opcode) {
case Opcodes.INVOKESPECIAL:
- invokespecial(owner, name, desc);
+ invokespecial(owner, name, desc, itf);
break;
case Opcodes.INVOKEVIRTUAL:
- invokevirtual(owner, name, desc);
+ invokevirtual(owner, name, desc, itf);
break;
case Opcodes.INVOKESTATIC:
- invokestatic(owner, name, desc);
+ invokestatic(owner, name, desc, itf);
break;
case Opcodes.INVOKEINTERFACE:
invokeinterface(owner, name, desc);
@@ -682,7 +708,7 @@ public void iconst(final int cst) {
} else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, cst);
} else {
- mv.visitLdcInsn(new Integer(cst));
+ mv.visitLdcInsn(cst);
}
}
@@ -690,7 +716,7 @@ public void lconst(final long cst) {
if (cst == 0L || cst == 1L) {
mv.visitInsn(Opcodes.LCONST_0 + (int) cst);
} else {
- mv.visitLdcInsn(new Long(cst));
+ mv.visitLdcInsn(cst);
}
}
@@ -699,7 +725,7 @@ public void fconst(final float cst) {
if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
mv.visitInsn(Opcodes.FCONST_0 + (int) cst);
} else {
- mv.visitLdcInsn(new Float(cst));
+ mv.visitLdcInsn(cst);
}
}
@@ -708,7 +734,7 @@ public void dconst(final double cst) {
if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
mv.visitInsn(Opcodes.DCONST_0 + (int) cst);
} else {
- mv.visitLdcInsn(new Double(cst));
+ mv.visitLdcInsn(cst);
}
}
@@ -985,24 +1011,78 @@ public void putfield(final String owner, final String name,
mv.visitFieldInsn(Opcodes.PUTFIELD, owner, name, desc);
}
+ @Deprecated
public void invokevirtual(final String owner, final String name,
final String desc) {
+ if (api >= Opcodes.ASM5) {
+ invokevirtual(owner, name, desc, false);
+ return;
+ }
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc);
}
+ public void invokevirtual(final String owner, final String name,
+ final String desc, final boolean itf) {
+ if (api < Opcodes.ASM5) {
+ if (itf) {
+ throw new IllegalArgumentException(
+ "INVOKEVIRTUAL on interfaces require ASM 5");
+ }
+ invokevirtual(owner, name, desc);
+ return;
+ }
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, itf);
+ }
+
+ @Deprecated
public void invokespecial(final String owner, final String name,
final String desc) {
- mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc);
+ if (api >= Opcodes.ASM5) {
+ invokespecial(owner, name, desc, false);
+ return;
+ }
+ mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false);
+ }
+
+ public void invokespecial(final String owner, final String name,
+ final String desc, final boolean itf) {
+ if (api < Opcodes.ASM5) {
+ if (itf) {
+ throw new IllegalArgumentException(
+ "INVOKESPECIAL on interfaces require ASM 5");
+ }
+ invokespecial(owner, name, desc);
+ return;
+ }
+ mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, itf);
}
+ @Deprecated
public void invokestatic(final String owner, final String name,
final String desc) {
- mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc);
+ if (api >= Opcodes.ASM5) {
+ invokestatic(owner, name, desc, false);
+ return;
+ }
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false);
+ }
+
+ public void invokestatic(final String owner, final String name,
+ final String desc, final boolean itf) {
+ if (api < Opcodes.ASM5) {
+ if (itf) {
+ throw new IllegalArgumentException(
+ "INVOKESTATIC on interfaces require ASM 5");
+ }
+ invokestatic(owner, name, desc);
+ return;
+ }
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, itf);
}
public void invokeinterface(final String owner, final String name,
final String desc) {
- mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc);
+ mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc, true);
}
public void invokedynamic(String name, String desc, Handle bsm,
diff --git a/src/java/nginx/clojure/asm/commons/JSRInlinerAdapter.java b/src/java/nginx/clojure/asm/commons/JSRInlinerAdapter.java
index 15ae01a2..593b2f99 100644
--- a/src/java/nginx/clojure/asm/commons/JSRInlinerAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/JSRInlinerAdapter.java
@@ -107,11 +107,16 @@ public class JSRInlinerAdapter extends MethodNode implements Opcodes {
* the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be
* null.
+ * @throws IllegalStateException
+ * If a subclass calls this constructor.
*/
public JSRInlinerAdapter(final MethodVisitor mv, final int access,
final String name, final String desc, final String signature,
final String[] exceptions) {
- this(Opcodes.ASM4, mv, access, name, desc, signature, exceptions);
+ this(Opcodes.ASM5, mv, access, name, desc, signature, exceptions);
+ if (getClass() != JSRInlinerAdapter.class) {
+ throw new IllegalStateException();
+ }
}
/**
@@ -119,7 +124,7 @@ public JSRInlinerAdapter(final MethodVisitor mv, final int access,
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param mv
* the MethodVisitor to send the resulting inlined
* method code to (use null for none).
diff --git a/src/java/nginx/clojure/asm/commons/LocalVariablesSorter.java b/src/java/nginx/clojure/asm/commons/LocalVariablesSorter.java
index 04373ba4..73dc44fd 100644
--- a/src/java/nginx/clojure/asm/commons/LocalVariablesSorter.java
+++ b/src/java/nginx/clojure/asm/commons/LocalVariablesSorter.java
@@ -29,10 +29,12 @@
*/
package nginx.clojure.asm.commons;
+import nginx.clojure.asm.AnnotationVisitor;
import nginx.clojure.asm.Label;
import nginx.clojure.asm.MethodVisitor;
import nginx.clojure.asm.Opcodes;
import nginx.clojure.asm.Type;
+import nginx.clojure.asm.TypePath;
/**
* A {@link MethodVisitor} that renumbers local variables in their order of
@@ -73,11 +75,6 @@ public class LocalVariablesSorter extends MethodVisitor {
*/
protected int nextLocal;
- /**
- * Indicates if at least one local variable has moved due to remapping.
- */
- private boolean changed;
-
/**
* Creates a new {@link LocalVariablesSorter}. Subclasses must not use
* this constructor. Instead, they must use the
@@ -89,10 +86,15 @@ public class LocalVariablesSorter extends MethodVisitor {
* the method's descriptor (see {@link Type Type}).
* @param mv
* the method visitor to which this adapter delegates calls.
+ * @throws IllegalStateException
+ * If a subclass calls this constructor.
*/
public LocalVariablesSorter(final int access, final String desc,
final MethodVisitor mv) {
- this(Opcodes.ASM4, access, desc, mv);
+ this(Opcodes.ASM5, access, desc, mv);
+ if (getClass() != LocalVariablesSorter.class) {
+ throw new IllegalStateException();
+ }
}
/**
@@ -100,7 +102,7 @@ public LocalVariablesSorter(final int access, final String desc,
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param access
* access flags of the adapted method.
* @param desc
@@ -171,6 +173,19 @@ public void visitLocalVariable(final String name, final String desc,
mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
}
+ @Override
+ public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
+ TypePath typePath, Label[] start, Label[] end, int[] index,
+ String desc, boolean visible) {
+ Type t = Type.getType(desc);
+ int[] newIndex = new int[index.length];
+ for (int i = 0; i < newIndex.length; ++i) {
+ newIndex[i] = remap(index[i], t);
+ }
+ return mv.visitLocalVariableAnnotation(typeRef, typePath, start, end,
+ newIndex, desc, visible);
+ }
+
@Override
public void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack) {
@@ -179,11 +194,6 @@ public void visitFrame(final int type, final int nLocal,
"ClassReader.accept() should be called with EXPAND_FRAMES flag");
}
- if (!changed) { // optimization for the case where mapping = identity
- mv.visitFrame(type, nLocal, local, nStack, stack);
- return;
- }
-
// creates a copy of newLocals
Object[] oldLocals = new Object[newLocals.length];
System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
@@ -279,7 +289,6 @@ public int newLocal(final Type type) {
int local = newLocalMapping(type);
setLocalType(local, type);
setFrameLocal(local, t);
- changed = true;
return local;
}
@@ -304,7 +313,7 @@ public int newLocal(final Type type) {
*/
protected void updateNewLocals(Object[] newLocals) {
}
-
+
/**
* Notifies subclasses that a local variable has been added or remapped. The
* default implementation of this method does nothing.
@@ -347,9 +356,6 @@ private int remap(final int var, final Type type) {
} else {
value--;
}
- if (value != var) {
- changed = true;
- }
return value;
}
diff --git a/src/java/nginx/clojure/asm/commons/Method.java b/src/java/nginx/clojure/asm/commons/Method.java
index b5cc0264..1b44123d 100644
--- a/src/java/nginx/clojure/asm/commons/Method.java
+++ b/src/java/nginx/clojure/asm/commons/Method.java
@@ -176,7 +176,7 @@ public static Method getMethod(final String method,
}
String returnType = method.substring(0, space);
String methodName = method.substring(space + 1, start - 1).trim();
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
sb.append('(');
int p;
do {
@@ -200,7 +200,7 @@ private static String map(final String type, final boolean defaultPackage) {
return type;
}
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
int index = 0;
while ((index = type.indexOf("[]", index) + 1) > 0) {
sb.append('[');
diff --git a/src/java/nginx/clojure/asm/commons/MethodRemapper.java b/src/java/nginx/clojure/asm/commons/MethodRemapper.java
new file mode 100644
index 00000000..53046699
--- /dev/null
+++ b/src/java/nginx/clojure/asm/commons/MethodRemapper.java
@@ -0,0 +1,223 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package nginx.clojure.asm.commons;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.Handle;
+import nginx.clojure.asm.Label;
+import nginx.clojure.asm.MethodVisitor;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
+
+/**
+ * A {@link LocalVariablesSorter} for type mapping.
+ *
+ * @author Eugene Kuleshov
+ */
+public class MethodRemapper extends MethodVisitor {
+
+ protected final Remapper remapper;
+
+ public MethodRemapper(final MethodVisitor mv, final Remapper remapper) {
+ this(Opcodes.ASM5, mv, remapper);
+ }
+
+ protected MethodRemapper(final int api, final MethodVisitor mv,
+ final Remapper remapper) {
+ super(api, mv);
+ this.remapper = remapper;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotationDefault() {
+ AnnotationVisitor av = super.visitAnnotationDefault();
+ return av == null ? av : new AnnotationRemapper(av, remapper);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ AnnotationVisitor av = super.visitAnnotation(remapper.mapDesc(desc),
+ visible);
+ return av == null ? av : new AnnotationRemapper(av, remapper);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
+ return av == null ? av : new AnnotationRemapper(av, remapper);
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(int parameter,
+ String desc, boolean visible) {
+ AnnotationVisitor av = super.visitParameterAnnotation(parameter,
+ remapper.mapDesc(desc), visible);
+ return av == null ? av : new AnnotationRemapper(av, remapper);
+ }
+
+ @Override
+ public void visitFrame(int type, int nLocal, Object[] local, int nStack,
+ Object[] stack) {
+ super.visitFrame(type, nLocal, remapEntries(nLocal, local), nStack,
+ remapEntries(nStack, stack));
+ }
+
+ private Object[] remapEntries(int n, Object[] entries) {
+ for (int i = 0; i < n; i++) {
+ if (entries[i] instanceof String) {
+ Object[] newEntries = new Object[n];
+ if (i > 0) {
+ System.arraycopy(entries, 0, newEntries, 0, i);
+ }
+ do {
+ Object t = entries[i];
+ newEntries[i++] = t instanceof String ? remapper
+ .mapType((String) t) : t;
+ } while (i < n);
+ return newEntries;
+ }
+ }
+ return entries;
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name,
+ String desc) {
+ super.visitFieldInsn(opcode, remapper.mapType(owner),
+ remapper.mapFieldName(owner, name, desc),
+ remapper.mapDesc(desc));
+ }
+
+ @Deprecated
+ @Override
+ public void visitMethodInsn(final int opcode, final String owner,
+ final String name, final String desc) {
+ if (api >= Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc,
+ opcode == Opcodes.INVOKEINTERFACE);
+ }
+
+ @Override
+ public void visitMethodInsn(final int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
+ if (api < Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ private void doVisitMethodInsn(int opcode, String owner, String name,
+ String desc, boolean itf) {
+ // Calling super.visitMethodInsn requires to call the correct version
+ // depending on this.api (otherwise infinite loops can occur). To
+ // simplify and to make it easier to automatically remove the backward
+ // compatibility code, we inline the code of the overridden method here.
+ // IMPORTANT: THIS ASSUMES THAT visitMethodInsn IS NOT OVERRIDDEN IN
+ // LocalVariableSorter.
+ if (mv != null) {
+ mv.visitMethodInsn(opcode, remapper.mapType(owner),
+ remapper.mapMethodName(owner, name, desc),
+ remapper.mapMethodDesc(desc), itf);
+ }
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+ Object... bsmArgs) {
+ for (int i = 0; i < bsmArgs.length; i++) {
+ bsmArgs[i] = remapper.mapValue(bsmArgs[i]);
+ }
+ super.visitInvokeDynamicInsn(
+ remapper.mapInvokeDynamicMethodName(name, desc),
+ remapper.mapMethodDesc(desc), (Handle) remapper.mapValue(bsm),
+ bsmArgs);
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String type) {
+ super.visitTypeInsn(opcode, remapper.mapType(type));
+ }
+
+ @Override
+ public void visitLdcInsn(Object cst) {
+ super.visitLdcInsn(remapper.mapValue(cst));
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ super.visitMultiANewArrayInsn(remapper.mapDesc(desc), dims);
+ }
+
+ @Override
+ public AnnotationVisitor visitInsnAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitInsnAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
+ return av == null ? av : new AnnotationRemapper(av, remapper);
+ }
+
+ @Override
+ public void visitTryCatchBlock(Label start, Label end, Label handler,
+ String type) {
+ super.visitTryCatchBlock(start, end, handler, type == null ? null
+ : remapper.mapType(type));
+ }
+
+ @Override
+ public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitTryCatchAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
+ return av == null ? av : new AnnotationRemapper(av, remapper);
+ }
+
+ @Override
+ public void visitLocalVariable(String name, String desc, String signature,
+ Label start, Label end, int index) {
+ super.visitLocalVariable(name, remapper.mapDesc(desc),
+ remapper.mapSignature(signature, true), start, end, index);
+ }
+
+ @Override
+ public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
+ TypePath typePath, Label[] start, Label[] end, int[] index,
+ String desc, boolean visible) {
+ AnnotationVisitor av = super.visitLocalVariableAnnotation(typeRef,
+ typePath, start, end, index, remapper.mapDesc(desc), visible);
+ return av == null ? av : new AnnotationRemapper(av, remapper);
+ }
+}
diff --git a/src/java/nginx/clojure/asm/commons/Remapper.java b/src/java/nginx/clojure/asm/commons/Remapper.java
index a4119cab..3ed1ea93 100644
--- a/src/java/nginx/clojure/asm/commons/Remapper.java
+++ b/src/java/nginx/clojure/asm/commons/Remapper.java
@@ -118,17 +118,17 @@ public String mapMethodDesc(String desc) {
}
Type[] args = Type.getArgumentTypes(desc);
- StringBuffer s = new StringBuffer("(");
+ StringBuilder sb = new StringBuilder("(");
for (int i = 0; i < args.length; i++) {
- s.append(mapDesc(args[i].getDescriptor()));
+ sb.append(mapDesc(args[i].getDescriptor()));
}
Type returnType = Type.getReturnType(desc);
if (returnType == Type.VOID_TYPE) {
- s.append(")V");
- return s.toString();
+ sb.append(")V");
+ return sb.toString();
}
- s.append(')').append(mapDesc(returnType.getDescriptor()));
- return s.toString();
+ sb.append(')').append(mapDesc(returnType.getDescriptor()));
+ return sb.toString();
}
public Object mapValue(Object value) {
@@ -139,17 +139,19 @@ public Object mapValue(Object value) {
Handle h = (Handle) value;
return new Handle(h.getTag(), mapType(h.getOwner()), mapMethodName(
h.getOwner(), h.getName(), h.getDesc()),
- mapMethodDesc(h.getDesc()));
+ mapMethodDesc(h.getDesc()), h.isInterface());
}
return value;
}
/**
- *
+ * @param signature
+ * signature for mapper
* @param typeSignature
* true if signature is a FieldTypeSignature, such as the
* signature parameter of the ClassVisitor.visitField or
* MethodVisitor.visitLocalVariable methods
+ * @return signature rewritten as a string
*/
public String mapSignature(String signature, boolean typeSignature) {
if (signature == null) {
@@ -157,7 +159,7 @@ public String mapSignature(String signature, boolean typeSignature) {
}
SignatureReader r = new SignatureReader(signature);
SignatureWriter w = new SignatureWriter();
- SignatureVisitor a = createRemappingSignatureAdapter(w);
+ SignatureVisitor a = createSignatureRemapper(w);
if (typeSignature) {
r.acceptType(a);
} else {
@@ -166,9 +168,18 @@ public String mapSignature(String signature, boolean typeSignature) {
return w.toString();
}
+ /**
+ * @deprecated use {@link #createSignatureRemapper} instead.
+ */
+ @Deprecated
protected SignatureVisitor createRemappingSignatureAdapter(
SignatureVisitor v) {
- return new RemappingSignatureAdapter(v, this);
+ return new SignatureRemapper(v, this);
+ }
+
+ protected SignatureVisitor createSignatureRemapper(
+ SignatureVisitor v) {
+ return createRemappingSignatureAdapter(v);
}
/**
@@ -216,6 +227,10 @@ public String mapFieldName(String owner, String name, String desc) {
/**
* Map type name to the new name. Subclasses can override.
+ *
+ * @param typeName
+ * the type name
+ * @return new name, default implementation is the identity.
*/
public String map(String typeName) {
return typeName;
diff --git a/src/java/nginx/clojure/asm/commons/RemappingAnnotationAdapter.java b/src/java/nginx/clojure/asm/commons/RemappingAnnotationAdapter.java
index 055b6c9e..ccd62c10 100644
--- a/src/java/nginx/clojure/asm/commons/RemappingAnnotationAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/RemappingAnnotationAdapter.java
@@ -36,15 +36,17 @@
/**
* An {@link AnnotationVisitor} adapter for type remapping.
*
+ * @deprecated use {@link AnnotationRemapper} instead.
* @author Eugene Kuleshov
*/
+@Deprecated
public class RemappingAnnotationAdapter extends AnnotationVisitor {
protected final Remapper remapper;
public RemappingAnnotationAdapter(final AnnotationVisitor av,
final Remapper remapper) {
- this(Opcodes.ASM4, av, remapper);
+ this(Opcodes.ASM5, av, remapper);
}
protected RemappingAnnotationAdapter(final int api,
diff --git a/src/java/nginx/clojure/asm/commons/RemappingClassAdapter.java b/src/java/nginx/clojure/asm/commons/RemappingClassAdapter.java
index 3ace8a22..a0d29332 100644
--- a/src/java/nginx/clojure/asm/commons/RemappingClassAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/RemappingClassAdapter.java
@@ -35,12 +35,15 @@
import nginx.clojure.asm.FieldVisitor;
import nginx.clojure.asm.MethodVisitor;
import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
/**
* A {@link ClassVisitor} for type remapping.
*
+ * @deprecated use {@link ClassRemapper} instead.
* @author Eugene Kuleshov
*/
+@Deprecated
public class RemappingClassAdapter extends ClassVisitor {
protected final Remapper remapper;
@@ -48,7 +51,7 @@ public class RemappingClassAdapter extends ClassVisitor {
protected String className;
public RemappingClassAdapter(final ClassVisitor cv, final Remapper remapper) {
- this(Opcodes.ASM4, cv, remapper);
+ this(Opcodes.ASM5, cv, remapper);
}
protected RemappingClassAdapter(final int api, final ClassVisitor cv,
@@ -68,8 +71,16 @@ public void visit(int version, int access, String name, String signature,
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
- AnnotationVisitor av;
- av = super.visitAnnotation(remapper.mapDesc(desc), visible);
+ AnnotationVisitor av = super.visitAnnotation(remapper.mapDesc(desc),
+ visible);
+ return av == null ? null : createRemappingAnnotationAdapter(av);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
return av == null ? null : createRemappingAnnotationAdapter(av);
}
diff --git a/src/java/nginx/clojure/asm/commons/RemappingFieldAdapter.java b/src/java/nginx/clojure/asm/commons/RemappingFieldAdapter.java
index bb7cf3af..e69da422 100644
--- a/src/java/nginx/clojure/asm/commons/RemappingFieldAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/RemappingFieldAdapter.java
@@ -33,18 +33,21 @@
import nginx.clojure.asm.AnnotationVisitor;
import nginx.clojure.asm.FieldVisitor;
import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
/**
* A {@link FieldVisitor} adapter for type remapping.
*
+ * @deprecated use {@link FieldRemapper} instead.
* @author Eugene Kuleshov
*/
+@Deprecated
public class RemappingFieldAdapter extends FieldVisitor {
private final Remapper remapper;
public RemappingFieldAdapter(final FieldVisitor fv, final Remapper remapper) {
- this(Opcodes.ASM4, fv, remapper);
+ this(Opcodes.ASM5, fv, remapper);
}
protected RemappingFieldAdapter(final int api, final FieldVisitor fv,
@@ -59,4 +62,12 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
visible);
return av == null ? null : new RemappingAnnotationAdapter(av, remapper);
}
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
+ return av == null ? null : new RemappingAnnotationAdapter(av, remapper);
+ }
}
diff --git a/src/java/nginx/clojure/asm/commons/RemappingMethodAdapter.java b/src/java/nginx/clojure/asm/commons/RemappingMethodAdapter.java
index 7763be22..f7f5f616 100644
--- a/src/java/nginx/clojure/asm/commons/RemappingMethodAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/RemappingMethodAdapter.java
@@ -35,19 +35,22 @@
import nginx.clojure.asm.Label;
import nginx.clojure.asm.MethodVisitor;
import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
/**
* A {@link LocalVariablesSorter} for type mapping.
*
+ * @deprecated use {@link MethodRemapper} instead.
* @author Eugene Kuleshov
*/
+@Deprecated
public class RemappingMethodAdapter extends LocalVariablesSorter {
protected final Remapper remapper;
public RemappingMethodAdapter(final int access, final String desc,
final MethodVisitor mv, final Remapper remapper) {
- this(Opcodes.ASM4, access, desc, mv, remapper);
+ this(Opcodes.ASM5, access, desc, mv, remapper);
}
protected RemappingMethodAdapter(final int api, final int access,
@@ -58,21 +61,29 @@ protected RemappingMethodAdapter(final int api, final int access,
@Override
public AnnotationVisitor visitAnnotationDefault() {
- AnnotationVisitor av = mv.visitAnnotationDefault();
+ AnnotationVisitor av = super.visitAnnotationDefault();
return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
- AnnotationVisitor av = mv.visitAnnotation(remapper.mapDesc(desc),
+ AnnotationVisitor av = super.visitAnnotation(remapper.mapDesc(desc),
visible);
return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
}
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
+ return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
+ }
+
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter,
String desc, boolean visible) {
- AnnotationVisitor av = mv.visitParameterAnnotation(parameter,
+ AnnotationVisitor av = super.visitParameterAnnotation(parameter,
remapper.mapDesc(desc), visible);
return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
}
@@ -110,12 +121,41 @@ public void visitFieldInsn(int opcode, String owner, String name,
remapper.mapDesc(desc));
}
+ @Deprecated
@Override
- public void visitMethodInsn(int opcode, String owner, String name,
- String desc) {
- super.visitMethodInsn(opcode, remapper.mapType(owner),
- remapper.mapMethodName(owner, name, desc),
- remapper.mapMethodDesc(desc));
+ public void visitMethodInsn(final int opcode, final String owner,
+ final String name, final String desc) {
+ if (api >= Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc,
+ opcode == Opcodes.INVOKEINTERFACE);
+ }
+
+ @Override
+ public void visitMethodInsn(final int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
+ if (api < Opcodes.ASM5) {
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ return;
+ }
+ doVisitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ private void doVisitMethodInsn(int opcode, String owner, String name,
+ String desc, boolean itf) {
+ // Calling super.visitMethodInsn requires to call the correct version
+ // depending on this.api (otherwise infinite loops can occur). To
+ // simplify and to make it easier to automatically remove the backward
+ // compatibility code, we inline the code of the overridden method here.
+ // IMPORTANT: THIS ASSUMES THAT visitMethodInsn IS NOT OVERRIDDEN IN
+ // LocalVariableSorter.
+ if (mv != null) {
+ mv.visitMethodInsn(opcode, remapper.mapType(owner),
+ remapper.mapMethodName(owner, name, desc),
+ remapper.mapMethodDesc(desc), itf);
+ }
}
@Override
@@ -145,6 +185,14 @@ public void visitMultiANewArrayInsn(String desc, int dims) {
super.visitMultiANewArrayInsn(remapper.mapDesc(desc), dims);
}
+ @Override
+ public AnnotationVisitor visitInsnAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitInsnAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
+ return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
+ }
+
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler,
String type) {
@@ -152,10 +200,27 @@ public void visitTryCatchBlock(Label start, Label end, Label handler,
: remapper.mapType(type));
}
+ @Override
+ public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ AnnotationVisitor av = super.visitTryCatchAnnotation(typeRef, typePath,
+ remapper.mapDesc(desc), visible);
+ return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
+ }
+
@Override
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index) {
super.visitLocalVariable(name, remapper.mapDesc(desc),
remapper.mapSignature(signature, true), start, end, index);
}
+
+ @Override
+ public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
+ TypePath typePath, Label[] start, Label[] end, int[] index,
+ String desc, boolean visible) {
+ AnnotationVisitor av = super.visitLocalVariableAnnotation(typeRef,
+ typePath, start, end, index, remapper.mapDesc(desc), visible);
+ return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
+ }
}
diff --git a/src/java/nginx/clojure/asm/commons/RemappingSignatureAdapter.java b/src/java/nginx/clojure/asm/commons/RemappingSignatureAdapter.java
index 7624080c..4435bc5b 100644
--- a/src/java/nginx/clojure/asm/commons/RemappingSignatureAdapter.java
+++ b/src/java/nginx/clojure/asm/commons/RemappingSignatureAdapter.java
@@ -36,8 +36,10 @@
/**
* A {@link SignatureVisitor} adapter for type mapping.
*
+ * @deprecated use {@link SignatureRemapper} instead.
* @author Eugene Kuleshov
*/
+@Deprecated
public class RemappingSignatureAdapter extends SignatureVisitor {
private final SignatureVisitor v;
@@ -48,7 +50,7 @@ public class RemappingSignatureAdapter extends SignatureVisitor {
public RemappingSignatureAdapter(final SignatureVisitor v,
final Remapper remapper) {
- this(Opcodes.ASM4, v, remapper);
+ this(Opcodes.ASM5, v, remapper);
}
protected RemappingSignatureAdapter(final int api,
diff --git a/src/java/nginx/clojure/asm/commons/SerialVersionUIDAdder.java b/src/java/nginx/clojure/asm/commons/SerialVersionUIDAdder.java
index 7fee4e35..38c5788d 100644
--- a/src/java/nginx/clojure/asm/commons/SerialVersionUIDAdder.java
+++ b/src/java/nginx/clojure/asm/commons/SerialVersionUIDAdder.java
@@ -166,9 +166,14 @@ public class SerialVersionUIDAdder extends ClassVisitor {
* @param cv
* a {@link ClassVisitor} to which this visitor will delegate
* calls.
+ * @throws IllegalStateException
+ * If a subclass calls this constructor.
*/
public SerialVersionUIDAdder(final ClassVisitor cv) {
- this(Opcodes.ASM4, cv);
+ this(Opcodes.ASM5, cv);
+ if (getClass() != SerialVersionUIDAdder.class) {
+ throw new IllegalStateException();
+ }
}
/**
@@ -176,7 +181,7 @@ public SerialVersionUIDAdder(final ClassVisitor cv) {
*
* @param api
* the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}.
+ * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param cv
* a {@link ClassVisitor} to which this visitor will delegate
* calls.
@@ -189,7 +194,7 @@ protected SerialVersionUIDAdder(final int api, final ClassVisitor cv) {
}
// ------------------------------------------------------------------------
- // Overriden methods
+ // Overridden methods
// ------------------------------------------------------------------------
/*
@@ -200,12 +205,14 @@ protected SerialVersionUIDAdder(final int api, final ClassVisitor cv) {
public void visit(final int version, final int access, final String name,
final String signature, final String superName,
final String[] interfaces) {
- computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0;
+ computeSVUID = (access & Opcodes.ACC_ENUM) == 0;
if (computeSVUID) {
this.name = name;
this.access = access;
- this.interfaces = interfaces;
+ this.interfaces = new String[interfaces.length];
+ System.arraycopy(interfaces, 0, this.interfaces, 0,
+ interfaces.length);
}
super.visit(version, access, name, signature, superName, interfaces);
@@ -330,8 +337,7 @@ public boolean hasSVUID() {
protected void addSVUID(long svuid) {
FieldVisitor fv = super.visitField(Opcodes.ACC_FINAL
- + Opcodes.ACC_STATIC, "serialVersionUID", "J", null, new Long(
- svuid));
+ + Opcodes.ACC_STATIC, "serialVersionUID", "J", null, svuid);
if (fv != null) {
fv.visitEnd();
}
@@ -361,6 +367,11 @@ protected long computeSVUID() throws IOException {
/*
* 2. The class modifiers written as a 32-bit integer.
*/
+ int access = this.access;
+ if ((access & Opcodes.ACC_INTERFACE) != 0) {
+ access = (svuidMethods.size() > 0) ? (access | Opcodes.ACC_ABSTRACT)
+ : (access & ~Opcodes.ACC_ABSTRACT);
+ }
dos.writeInt(access
& (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
| Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT));
diff --git a/src/java/nginx/clojure/asm/commons/SignatureRemapper.java b/src/java/nginx/clojure/asm/commons/SignatureRemapper.java
new file mode 100644
index 00000000..befc6945
--- /dev/null
+++ b/src/java/nginx/clojure/asm/commons/SignatureRemapper.java
@@ -0,0 +1,159 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package nginx.clojure.asm.commons;
+
+import java.util.Stack;
+
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.signature.SignatureVisitor;
+
+/**
+ * A {@link SignatureVisitor} adapter for type mapping.
+ *
+ * @author Eugene Kuleshov
+ */
+public class SignatureRemapper extends SignatureVisitor {
+
+ private final SignatureVisitor v;
+
+ private final Remapper remapper;
+
+ private Stack classNames = new Stack();
+
+ public SignatureRemapper(final SignatureVisitor v, final Remapper remapper) {
+ this(Opcodes.ASM5, v, remapper);
+ }
+
+ protected SignatureRemapper(final int api, final SignatureVisitor v,
+ final Remapper remapper) {
+ super(api);
+ this.v = v;
+ this.remapper = remapper;
+ }
+
+ @Override
+ public void visitClassType(String name) {
+ classNames.push(name);
+ v.visitClassType(remapper.mapType(name));
+ }
+
+ @Override
+ public void visitInnerClassType(String name) {
+ String outerClassName = classNames.pop();
+ String className = outerClassName + '$' + name;
+ classNames.push(className);
+ String remappedOuter = remapper.mapType(outerClassName) + '$';
+ String remappedName = remapper.mapType(className);
+ int index = remappedName.startsWith(remappedOuter) ? remappedOuter
+ .length() : remappedName.lastIndexOf('$') + 1;
+ v.visitInnerClassType(remappedName.substring(index));
+ }
+
+ @Override
+ public void visitFormalTypeParameter(String name) {
+ v.visitFormalTypeParameter(name);
+ }
+
+ @Override
+ public void visitTypeVariable(String name) {
+ v.visitTypeVariable(name);
+ }
+
+ @Override
+ public SignatureVisitor visitArrayType() {
+ v.visitArrayType();
+ return this;
+ }
+
+ @Override
+ public void visitBaseType(char descriptor) {
+ v.visitBaseType(descriptor);
+ }
+
+ @Override
+ public SignatureVisitor visitClassBound() {
+ v.visitClassBound();
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitExceptionType() {
+ v.visitExceptionType();
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitInterface() {
+ v.visitInterface();
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitInterfaceBound() {
+ v.visitInterfaceBound();
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitParameterType() {
+ v.visitParameterType();
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitReturnType() {
+ v.visitReturnType();
+ return this;
+ }
+
+ @Override
+ public SignatureVisitor visitSuperclass() {
+ v.visitSuperclass();
+ return this;
+ }
+
+ @Override
+ public void visitTypeArgument() {
+ v.visitTypeArgument();
+ }
+
+ @Override
+ public SignatureVisitor visitTypeArgument(char wildcard) {
+ v.visitTypeArgument(wildcard);
+ return this;
+ }
+
+ @Override
+ public void visitEnd() {
+ v.visitEnd();
+ classNames.pop();
+ }
+}
diff --git a/src/java/nginx/clojure/asm/commons/SimpleRemapper.java b/src/java/nginx/clojure/asm/commons/SimpleRemapper.java
index 6e3818e1..9264cb0f 100644
--- a/src/java/nginx/clojure/asm/commons/SimpleRemapper.java
+++ b/src/java/nginx/clojure/asm/commons/SimpleRemapper.java
@@ -35,7 +35,7 @@
/**
* A {@link Remapper} using a {@link Map} to define its mapping.
- *
+ *
* @author Eugene Kuleshov
*/
public class SimpleRemapper extends Remapper {
@@ -56,6 +56,12 @@ public String mapMethodName(String owner, String name, String desc) {
return s == null ? name : s;
}
+ @Override
+ public String mapInvokeDynamicMethodName(String name, String desc) {
+ String s = map('.' + name + desc);
+ return s == null ? name : s;
+ }
+
@Override
public String mapFieldName(String owner, String name, String desc) {
String s = map(owner + '.' + name);
diff --git a/src/java/nginx/clojure/asm/commons/StaticInitMerger.java b/src/java/nginx/clojure/asm/commons/StaticInitMerger.java
index 7f0ba19b..4ecb20b4 100644
--- a/src/java/nginx/clojure/asm/commons/StaticInitMerger.java
+++ b/src/java/nginx/clojure/asm/commons/StaticInitMerger.java
@@ -49,7 +49,7 @@ public class StaticInitMerger extends ClassVisitor {
private int counter;
public StaticInitMerger(final String prefix, final ClassVisitor cv) {
- this(Opcodes.ASM4, prefix, cv);
+ this(Opcodes.ASM5, prefix, cv);
}
protected StaticInitMerger(final int api, final String prefix,
@@ -78,7 +78,8 @@ public MethodVisitor visitMethod(final int access, final String name,
if (clinit == null) {
clinit = cv.visitMethod(a, name, desc, null, null);
}
- clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc);
+ clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc,
+ false);
} else {
mv = cv.visitMethod(access, name, desc, signature, exceptions);
}
diff --git a/src/java/nginx/clojure/asm/commons/TryCatchBlockSorter.java b/src/java/nginx/clojure/asm/commons/TryCatchBlockSorter.java
index 92739c43..65450797 100644
--- a/src/java/nginx/clojure/asm/commons/TryCatchBlockSorter.java
+++ b/src/java/nginx/clojure/asm/commons/TryCatchBlockSorter.java
@@ -57,7 +57,7 @@ public class TryCatchBlockSorter extends MethodNode {
public TryCatchBlockSorter(final MethodVisitor mv, final int access,
final String name, final String desc, final String signature,
final String[] exceptions) {
- this(Opcodes.ASM4, mv, access, name, desc, signature, exceptions);
+ this(Opcodes.ASM5, mv, access, name, desc, signature, exceptions);
}
protected TryCatchBlockSorter(final int api, final MethodVisitor mv,
@@ -85,6 +85,10 @@ private int blockLength(TryCatchBlockNode block) {
}
};
Collections.sort(tryCatchBlocks, comp);
+ // Updates the 'target' of each try catch block annotation.
+ for (int i = 0; i < tryCatchBlocks.size(); ++i) {
+ tryCatchBlocks.get(i).updateIndex(i);
+ }
if (mv != null) {
accept(mv);
}
diff --git a/src/java/nginx/clojure/asm/commons/package.html b/src/java/nginx/clojure/asm/commons/package.html
new file mode 100644
index 00000000..4ce0db85
--- /dev/null
+++ b/src/java/nginx/clojure/asm/commons/package.html
@@ -0,0 +1,48 @@
+
+
+
+Provides some useful class and method adapters. The preferred way of using
+these adapters is by chaining them together and to custom adapters (instead of
+inheriting from them). Indeed this approach provides more combination
+possibilities than inheritance. For instance, suppose you want to implement an
+adapter MyAdapter than needs sorted local variables and intermediate stack map
+frame values taking into account the local variables sort. By using inheritance,
+this would require MyAdapter to extend AnalyzerAdapter, itself extending
+LocalVariablesSorter. But AnalyzerAdapter is not a subclass of
+LocalVariablesSorter, so this is not possible. On the contrary, by using
+delegation, you can make LocalVariablesSorter delegate to AnalyzerAdapter,
+itself delegating to MyAdapter. In this case AnalyzerAdapter computes
+intermediate frames based on the output of LocalVariablesSorter, and MyAdapter
+can add new locals by calling the newLocal method on LocalVariablesSorter, and
+can get the stack map frame state before each instruction by reading the locals
+and stack fields in AnalyzerAdapter (this requires references from MyAdapter
+back to LocalVariablesSorter and AnalyzerAdapter).
+
\ No newline at end of file
diff --git a/src/java/nginx/clojure/asm/optimizer/AnnotationConstantsCollector.java b/src/java/nginx/clojure/asm/optimizer/AnnotationConstantsCollector.java
new file mode 100644
index 00000000..0b4b35aa
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/AnnotationConstantsCollector.java
@@ -0,0 +1,147 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.Type;
+
+/**
+ * An {@link AnnotationVisitor} that collects the {@link Constant}s of the
+ * annotations it visits.
+ *
+ * @author Eric Bruneton
+ */
+public class AnnotationConstantsCollector extends AnnotationVisitor {
+
+ private final ConstantPool cp;
+
+ public AnnotationConstantsCollector(final AnnotationVisitor av,
+ final ConstantPool cp) {
+ super(Opcodes.ASM5, av);
+ this.cp = cp;
+ }
+
+ @Override
+ public void visit(final String name, final Object value) {
+ if (name != null) {
+ cp.newUTF8(name);
+ }
+ if (value instanceof Byte) {
+ cp.newInteger(((Byte) value).byteValue());
+ } else if (value instanceof Boolean) {
+ cp.newInteger(((Boolean) value).booleanValue() ? 1 : 0);
+ } else if (value instanceof Character) {
+ cp.newInteger(((Character) value).charValue());
+ } else if (value instanceof Short) {
+ cp.newInteger(((Short) value).shortValue());
+ } else if (value instanceof Type) {
+ cp.newUTF8(((Type) value).getDescriptor());
+ } else if (value instanceof byte[]) {
+ byte[] v = (byte[]) value;
+ for (int i = 0; i < v.length; i++) {
+ cp.newInteger(v[i]);
+ }
+ } else if (value instanceof boolean[]) {
+ boolean[] v = (boolean[]) value;
+ for (int i = 0; i < v.length; i++) {
+ cp.newInteger(v[i] ? 1 : 0);
+ }
+ } else if (value instanceof short[]) {
+ short[] v = (short[]) value;
+ for (int i = 0; i < v.length; i++) {
+ cp.newInteger(v[i]);
+ }
+ } else if (value instanceof char[]) {
+ char[] v = (char[]) value;
+ for (int i = 0; i < v.length; i++) {
+ cp.newInteger(v[i]);
+ }
+ } else if (value instanceof int[]) {
+ int[] v = (int[]) value;
+ for (int i = 0; i < v.length; i++) {
+ cp.newInteger(v[i]);
+ }
+ } else if (value instanceof long[]) {
+ long[] v = (long[]) value;
+ for (int i = 0; i < v.length; i++) {
+ cp.newLong(v[i]);
+ }
+ } else if (value instanceof float[]) {
+ float[] v = (float[]) value;
+ for (int i = 0; i < v.length; i++) {
+ cp.newFloat(v[i]);
+ }
+ } else if (value instanceof double[]) {
+ double[] v = (double[]) value;
+ for (int i = 0; i < v.length; i++) {
+ cp.newDouble(v[i]);
+ }
+ } else {
+ cp.newConst(value);
+ }
+ av.visit(name, value);
+ }
+
+ @Override
+ public void visitEnum(final String name, final String desc,
+ final String value) {
+ if (name != null) {
+ cp.newUTF8(name);
+ }
+ cp.newUTF8(desc);
+ cp.newUTF8(value);
+ av.visitEnum(name, desc, value);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(final String name,
+ final String desc) {
+ if (name != null) {
+ cp.newUTF8(name);
+ }
+ cp.newUTF8(desc);
+ return new AnnotationConstantsCollector(av.visitAnnotation(name, desc),
+ cp);
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(final String name) {
+ if (name != null) {
+ cp.newUTF8(name);
+ }
+ return new AnnotationConstantsCollector(av.visitArray(name), cp);
+ }
+
+ @Override
+ public void visitEnd() {
+ av.visitEnd();
+ }
+}
diff --git a/src/java/nginx/clojure/asm/optimizer/ClassConstantsCollector.java b/src/java/nginx/clojure/asm/optimizer/ClassConstantsCollector.java
new file mode 100644
index 00000000..1a842084
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/ClassConstantsCollector.java
@@ -0,0 +1,198 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.Attribute;
+import nginx.clojure.asm.ClassVisitor;
+import nginx.clojure.asm.FieldVisitor;
+import nginx.clojure.asm.MethodVisitor;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
+
+/**
+ * A {@link ClassVisitor} that collects the {@link Constant}s of the classes it
+ * visits.
+ *
+ * @author Eric Bruneton
+ */
+public class ClassConstantsCollector extends ClassVisitor {
+
+ private final ConstantPool cp;
+
+ public ClassConstantsCollector(final ClassVisitor cv, final ConstantPool cp) {
+ super(Opcodes.ASM5, cv);
+ this.cp = cp;
+ }
+
+ @Override
+ public void visit(final int version, final int access, final String name,
+ final String signature, final String superName,
+ final String[] interfaces) {
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ cp.newUTF8("Deprecated");
+ }
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+ cp.newUTF8("Synthetic");
+ }
+ cp.newClass(name);
+ if (signature != null) {
+ cp.newUTF8("Signature");
+ cp.newUTF8(signature);
+ }
+ if (superName != null) {
+ cp.newClass(superName);
+ }
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; ++i) {
+ cp.newClass(interfaces[i]);
+ }
+ }
+ cv.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public void visitSource(final String source, final String debug) {
+ if (source != null) {
+ cp.newUTF8("SourceFile");
+ cp.newUTF8(source);
+ }
+ if (debug != null) {
+ cp.newUTF8("SourceDebugExtension");
+ }
+ cv.visitSource(source, debug);
+ }
+
+ @Override
+ public void visitOuterClass(final String owner, final String name,
+ final String desc) {
+ cp.newUTF8("EnclosingMethod");
+ cp.newClass(owner);
+ if (name != null && desc != null) {
+ cp.newNameType(name, desc);
+ }
+ cv.visitOuterClass(owner, name, desc);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(final String desc,
+ final boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleAnnotations");
+ }
+ return new AnnotationConstantsCollector(cv.visitAnnotation(desc,
+ visible), cp);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleTypeAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleTypeAnnotations");
+ }
+ return new AnnotationConstantsCollector(cv.visitAnnotation(desc,
+ visible), cp);
+ }
+
+ @Override
+ public void visitAttribute(final Attribute attr) {
+ // can do nothing
+ cv.visitAttribute(attr);
+ }
+
+ @Override
+ public void visitInnerClass(final String name, final String outerName,
+ final String innerName, final int access) {
+ cp.newUTF8("InnerClasses");
+ if (name != null) {
+ cp.newClass(name);
+ }
+ if (outerName != null) {
+ cp.newClass(outerName);
+ }
+ if (innerName != null) {
+ cp.newUTF8(innerName);
+ }
+ cv.visitInnerClass(name, outerName, innerName, access);
+ }
+
+ @Override
+ public FieldVisitor visitField(final int access, final String name,
+ final String desc, final String signature, final Object value) {
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+ cp.newUTF8("Synthetic");
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ cp.newUTF8("Deprecated");
+ }
+ cp.newUTF8(name);
+ cp.newUTF8(desc);
+ if (signature != null) {
+ cp.newUTF8("Signature");
+ cp.newUTF8(signature);
+ }
+ if (value != null) {
+ cp.newConst(value);
+ }
+ return new FieldConstantsCollector(cv.visitField(access, name, desc,
+ signature, value), cp);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name,
+ final String desc, final String signature, final String[] exceptions) {
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+ cp.newUTF8("Synthetic");
+ }
+ if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+ cp.newUTF8("Deprecated");
+ }
+ cp.newUTF8(name);
+ cp.newUTF8(desc);
+ if (signature != null) {
+ cp.newUTF8("Signature");
+ cp.newUTF8(signature);
+ }
+ if (exceptions != null) {
+ cp.newUTF8("Exceptions");
+ for (int i = 0; i < exceptions.length; ++i) {
+ cp.newClass(exceptions[i]);
+ }
+ }
+ return new MethodConstantsCollector(cv.visitMethod(access, name, desc,
+ signature, exceptions), cp);
+ }
+}
diff --git a/src/java/nginx/clojure/asm/optimizer/ClassOptimizer.java b/src/java/nginx/clojure/asm/optimizer/ClassOptimizer.java
new file mode 100644
index 00000000..1cbdc8ad
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/ClassOptimizer.java
@@ -0,0 +1,259 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.Attribute;
+import nginx.clojure.asm.ClassVisitor;
+import nginx.clojure.asm.FieldVisitor;
+import nginx.clojure.asm.Label;
+import nginx.clojure.asm.MethodVisitor;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
+import nginx.clojure.asm.commons.ClassRemapper;
+import nginx.clojure.asm.commons.Remapper;
+
+/**
+ * A {@link ClassVisitor} that renames fields and methods, and removes debug
+ * info.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class ClassOptimizer extends ClassRemapper {
+
+ private String pkgName;
+ String clsName;
+
+ boolean isInterface = false;
+ boolean hasClinitMethod = false;
+ List syntheticClassFields = new ArrayList();
+
+ public ClassOptimizer(final ClassVisitor cv, final Remapper remapper) {
+ super(Opcodes.ASM5, cv, remapper);
+ }
+
+ FieldVisitor syntheticFieldVisitor(final int access, final String name,
+ final String desc) {
+ return super.visitField(access, name, desc, null, null);
+ }
+
+ // ------------------------------------------------------------------------
+ // Overridden methods
+ // ------------------------------------------------------------------------
+
+ @Override
+ public void visit(final int version, final int access, final String name,
+ final String signature, final String superName,
+ final String[] interfaces) {
+ super.visit(Opcodes.V1_2, access, name, null, superName, interfaces);
+ int index = name.lastIndexOf('/');
+ if (index > 0) {
+ pkgName = name.substring(0, index);
+ } else {
+ pkgName = "";
+ }
+ clsName = name;
+ isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
+ }
+
+ @Override
+ public void visitSource(final String source, final String debug) {
+ // remove debug info
+ }
+
+ @Override
+ public void visitOuterClass(final String owner, final String name,
+ final String desc) {
+ // remove debug info
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(final String desc,
+ final boolean visible) {
+ // remove annotations
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ // remove annotations
+ return null;
+ }
+
+ @Override
+ public void visitAttribute(final Attribute attr) {
+ // remove non standard attributes
+ }
+
+ @Override
+ public void visitInnerClass(final String name, final String outerName,
+ final String innerName, final int access) {
+ // remove debug info
+ }
+
+ @Override
+ public FieldVisitor visitField(final int access, final String name,
+ final String desc, final String signature, final Object value) {
+ String s = remapper.mapFieldName(className, name, desc);
+ if ("-".equals(s)) {
+ return null;
+ }
+ if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
+ if ((access & Opcodes.ACC_FINAL) != 0
+ && (access & Opcodes.ACC_STATIC) != 0 && desc.length() == 1) {
+ return null;
+ }
+ if ("org/objectweb/asm".equals(pkgName) && s.equals(name)) {
+ System.out.println("INFO: " + clsName + "." + s
+ + " could be renamed");
+ }
+ super.visitField(access, name, desc, null, value);
+ } else {
+ if (!s.equals(name)) {
+ throw new RuntimeException("The public or protected field "
+ + className + '.' + name + " must not be renamed.");
+ }
+ super.visitField(access, name, desc, null, value);
+ }
+ return null; // remove debug info
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name,
+ final String desc, final String signature, final String[] exceptions) {
+ String s = remapper.mapMethodName(className, name, desc);
+ if ("-".equals(s)) {
+ return null;
+ }
+ if (name.equals("") && !isInterface) {
+ hasClinitMethod = true;
+ MethodVisitor mv = super.visitMethod(access, name, desc, null,
+ exceptions);
+ return new MethodVisitor(Opcodes.ASM5, mv) {
+ @Override
+ public void visitCode() {
+ super.visitCode();
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, clsName,
+ "_clinit_", "()V", false);
+ }
+ };
+ }
+
+ if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
+ if ("org/objectweb/asm".equals(pkgName) && !name.startsWith("<")
+ && s.equals(name)) {
+ System.out.println("INFO: " + clsName + "." + s
+ + " could be renamed");
+ }
+ return super.visitMethod(access, name, desc, null, exceptions);
+ } else {
+ if (!s.equals(name)) {
+ throw new RuntimeException("The public or protected method "
+ + className + '.' + name + desc
+ + " must not be renamed.");
+ }
+ return super.visitMethod(access, name, desc, null, exceptions);
+ }
+ }
+
+ @Override
+ protected MethodVisitor createMethodRemapper(MethodVisitor mv) {
+ return new MethodOptimizer(this, mv, remapper);
+ }
+
+ @Override
+ public void visitEnd() {
+ if (syntheticClassFields.isEmpty()) {
+ if (hasClinitMethod) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_STATIC
+ | Opcodes.ACC_SYNTHETIC, "_clinit_", "()V", null, null);
+ mv.visitCode();
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ } else {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_STATIC
+ | Opcodes.ACC_SYNTHETIC, "class$",
+ "(Ljava/lang/String;)Ljava/lang/Class;", null, null);
+ mv.visitCode();
+ Label l0 = new Label();
+ Label l1 = new Label();
+ Label l2 = new Label();
+ mv.visitTryCatchBlock(l0, l1, l2,
+ "java/lang/ClassNotFoundException");
+ mv.visitLabel(l0);
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Class",
+ "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
+ mv.visitLabel(l1);
+ mv.visitInsn(Opcodes.ARETURN);
+ mv.visitLabel(l2);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "java/lang/ClassNotFoundException", "getMessage",
+ "()Ljava/lang/String;", false);
+ mv.visitVarInsn(Opcodes.ASTORE, 1);
+ mv.visitTypeInsn(Opcodes.NEW, "java/lang/NoClassDefFoundError");
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitVarInsn(Opcodes.ALOAD, 1);
+ mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
+ "java/lang/NoClassDefFoundError", "",
+ "(Ljava/lang/String;)V", false);
+ mv.visitInsn(Opcodes.ATHROW);
+ mv.visitMaxs(3, 2);
+ mv.visitEnd();
+
+ if (hasClinitMethod) {
+ mv = cv.visitMethod(Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE,
+ "_clinit_", "()V", null, null);
+ } else {
+ mv = cv.visitMethod(Opcodes.ACC_STATIC, "", "()V",
+ null, null);
+ }
+ for (String ldcName : syntheticClassFields) {
+ String fieldName = "class$" + ldcName.replace('/', '$');
+ mv.visitLdcInsn(ldcName.replace('/', '.'));
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, clsName, "class$",
+ "(Ljava/lang/String;)Ljava/lang/Class;", false);
+ mv.visitFieldInsn(Opcodes.PUTSTATIC, clsName, fieldName,
+ "Ljava/lang/Class;");
+ }
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(1, 0);
+ mv.visitEnd();
+ }
+ super.visitEnd();
+ }
+}
diff --git a/src/java/nginx/clojure/asm/optimizer/Constant.java b/src/java/nginx/clojure/asm/optimizer/Constant.java
new file mode 100644
index 00000000..05d403c2
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/Constant.java
@@ -0,0 +1,336 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import java.util.Arrays;
+
+import nginx.clojure.asm.ClassWriter;
+import nginx.clojure.asm.Handle;
+
+/**
+ * A constant pool item.
+ *
+ * @author Eric Bruneton
+ */
+class Constant {
+
+ /**
+ * Type of this constant pool item. A single class is used to represent all
+ * constant pool item types, in order to minimize the bytecode size of this
+ * package. The value of this field is I, J, F, D, S, s, C, T, G, M, N, y,
+ * t, [h..r] (for Constant Integer, Long, Float, Double, STR, UTF8, Class,
+ * NameType, Fieldref, Methodref, InterfaceMethodref, InvokeDynamic,
+ * MethodType and MethodHandle constant pool items respectively).
+ *
+ * The 9 variable of MethodHandle constants are stored between h and r
+ * following this table
+ * tag type interface
+ * H_GETFIELD 1 h false
+ * H_GETSTATIC 2 i false
+ * H_PUTFIELD 3 j false
+ * H_PUTSTATIC 4 k false
+ * H_INVOKEVIRTUAL 5 l false
+ * H_INVOKESTATIC 6 m false
+ * H_INVOKESPECIAL 7 n false
+ * H_NEWINVOKESPECIAL 8 o false
+ * H_INVOKEINTERFACE 9 p true
+ * H_INVOKESTATIC 6 q true
+ * H_INVOKESPECIAL 7 r true
+ */
+ char type;
+
+ /**
+ * Value of this item, for an integer item.
+ */
+ int intVal;
+
+ /**
+ * Value of this item, for a long item.
+ */
+ long longVal;
+
+ /**
+ * Value of this item, for a float item.
+ */
+ float floatVal;
+
+ /**
+ * Value of this item, for a double item.
+ */
+ double doubleVal;
+
+ /**
+ * First part of the value of this item, for items that do not hold a
+ * primitive value.
+ */
+ String strVal1;
+
+ /**
+ * Second part of the value of this item, for items that do not hold a
+ * primitive value.
+ */
+ String strVal2;
+
+ /**
+ * Third part of the value of this item, for items that do not hold a
+ * primitive value.
+ */
+ Object objVal3;
+
+ /**
+ * InvokeDynamic's constant values.
+ */
+ Object[] objVals;
+
+ /**
+ * The hash code value of this constant pool item.
+ */
+ int hashCode;
+
+ Constant() {
+ }
+
+ Constant(final Constant i) {
+ type = i.type;
+ intVal = i.intVal;
+ longVal = i.longVal;
+ floatVal = i.floatVal;
+ doubleVal = i.doubleVal;
+ strVal1 = i.strVal1;
+ strVal2 = i.strVal2;
+ objVal3 = i.objVal3;
+ objVals = i.objVals;
+ hashCode = i.hashCode;
+ }
+
+ /**
+ * Sets this item to an integer item.
+ *
+ * @param intVal
+ * the value of this item.
+ */
+ void set(final int intVal) {
+ this.type = 'I';
+ this.intVal = intVal;
+ this.hashCode = 0x7FFFFFFF & (type + intVal);
+ }
+
+ /**
+ * Sets this item to a long item.
+ *
+ * @param longVal
+ * the value of this item.
+ */
+ void set(final long longVal) {
+ this.type = 'J';
+ this.longVal = longVal;
+ this.hashCode = 0x7FFFFFFF & (type + (int) longVal);
+ }
+
+ /**
+ * Sets this item to a float item.
+ *
+ * @param floatVal
+ * the value of this item.
+ */
+ void set(final float floatVal) {
+ this.type = 'F';
+ this.floatVal = floatVal;
+ this.hashCode = 0x7FFFFFFF & (type + (int) floatVal);
+ }
+
+ /**
+ * Sets this item to a double item.
+ *
+ * @param doubleVal
+ * the value of this item.
+ */
+ void set(final double doubleVal) {
+ this.type = 'D';
+ this.doubleVal = doubleVal;
+ this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal);
+ }
+
+ /**
+ * Sets this item to an item that do not hold a primitive value.
+ *
+ * @param type
+ * the type of this item.
+ * @param strVal1
+ * first part of the value of this item.
+ * @param strVal2
+ * second part of the value of this item.
+ * @param strVal3
+ * third part of the value of this item.
+ */
+ void set(final char type, final String strVal1, final String strVal2,
+ final String strVal3) {
+ this.type = type;
+ this.strVal1 = strVal1;
+ this.strVal2 = strVal2;
+ this.objVal3 = strVal3;
+ switch (type) {
+ case 's':
+ case 'S':
+ case 'C':
+ case 't':
+ hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
+ return;
+ case 'T':
+ hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
+ * strVal2.hashCode());
+ return;
+ // case 'G':
+ // case 'M':
+ // case 'N':
+ // case 'h' ... 'r':
+ default:
+ hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
+ * strVal2.hashCode() * strVal3.hashCode());
+ }
+ }
+
+ /**
+ * Set this item to an InvokeDynamic item.
+ *
+ * @param name
+ * invokedynamic's name.
+ * @param desc
+ * invokedynamic's descriptor.
+ * @param bsm
+ * bootstrap method.
+ * @param bsmArgs
+ * bootstrap method constant arguments.
+ */
+ void set(final String name, final String desc, final Handle bsm,
+ final Object[] bsmArgs) {
+ this.type = 'y';
+ this.strVal1 = name;
+ this.strVal2 = desc;
+ this.objVal3 = bsm;
+ this.objVals = bsmArgs;
+
+ int hashCode = 'y' + name.hashCode() * desc.hashCode() * bsm.hashCode();
+ for (int i = 0; i < bsmArgs.length; i++) {
+ hashCode *= bsmArgs[i].hashCode();
+ }
+ this.hashCode = 0x7FFFFFFF & hashCode;
+ }
+
+ void write(final ClassWriter cw) {
+ switch (type) {
+ case 'I':
+ cw.newConst(intVal);
+ break;
+ case 'J':
+ cw.newConst(longVal);
+ break;
+ case 'F':
+ cw.newConst(floatVal);
+ break;
+ case 'D':
+ cw.newConst(doubleVal);
+ break;
+ case 'S':
+ cw.newConst(strVal1);
+ break;
+ case 's':
+ cw.newUTF8(strVal1);
+ break;
+ case 'C':
+ cw.newClass(strVal1);
+ break;
+ case 'T':
+ cw.newNameType(strVal1, strVal2);
+ break;
+ case 'G':
+ cw.newField(strVal1, strVal2, (String) objVal3);
+ break;
+ case 'M':
+ cw.newMethod(strVal1, strVal2, (String) objVal3, false);
+ break;
+ case 'N':
+ cw.newMethod(strVal1, strVal2, (String) objVal3, true);
+ break;
+ case 'y':
+ cw.newInvokeDynamic(strVal1, strVal2, (Handle) objVal3, objVals);
+ break;
+ case 't':
+ cw.newMethodType(strVal1);
+ break;
+ default: // 'h' ... 'r' : handle
+ cw.newHandle(type - 'h' + 1 - ((type >= 'q')? 4: 0), strVal1, strVal2, (String) objVal3, type >= 'p');
+ }
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof Constant)) {
+ return false;
+ }
+ Constant c = (Constant) o;
+ if (c.type == type) {
+ switch (type) {
+ case 'I':
+ return c.intVal == intVal;
+ case 'J':
+ return c.longVal == longVal;
+ case 'F':
+ return Float.compare(c.floatVal, floatVal) == 0;
+ case 'D':
+ return Double.compare(c.doubleVal, doubleVal) == 0;
+ case 's':
+ case 'S':
+ case 'C':
+ case 't':
+ return c.strVal1.equals(strVal1);
+ case 'T':
+ return c.strVal1.equals(strVal1) && c.strVal2.equals(strVal2);
+ case 'y':
+ return c.strVal1.equals(strVal1) && c.strVal2.equals(strVal2)
+ && c.objVal3.equals(objVal3)
+ && Arrays.equals(c.objVals, objVals);
+ // case 'G':
+ // case 'M':
+ // case 'N':
+ // case 'h' ... 'r':
+ default:
+ return c.strVal1.equals(strVal1) && c.strVal2.equals(strVal2)
+ && c.objVal3.equals(objVal3);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+}
diff --git a/src/java/nginx/clojure/asm/optimizer/ConstantPool.java b/src/java/nginx/clojure/asm/optimizer/ConstantPool.java
new file mode 100644
index 00000000..72d97c10
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/ConstantPool.java
@@ -0,0 +1,251 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import java.util.HashMap;
+
+import nginx.clojure.asm.Handle;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.Type;
+
+/**
+ * A constant pool.
+ *
+ * @author Eric Bruneton
+ */
+public class ConstantPool extends HashMap {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Constant key1 = new Constant();
+
+ private final Constant key2 = new Constant();
+
+ private final Constant key3 = new Constant();
+
+ private final Constant key4 = new Constant();
+
+ private final Constant key5 = new Constant();
+
+ public Constant newInteger(final int value) {
+ key1.set(value);
+ Constant result = get(key1);
+ if (result == null) {
+ result = new Constant(key1);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newFloat(final float value) {
+ key1.set(value);
+ Constant result = get(key1);
+ if (result == null) {
+ result = new Constant(key1);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newLong(final long value) {
+ key1.set(value);
+ Constant result = get(key1);
+ if (result == null) {
+ result = new Constant(key1);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newDouble(final double value) {
+ key1.set(value);
+ Constant result = get(key1);
+ if (result == null) {
+ result = new Constant(key1);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newUTF8(final String value) {
+ key1.set('s', value, null, null);
+ Constant result = get(key1);
+ if (result == null) {
+ result = new Constant(key1);
+ put(result);
+ }
+ return result;
+ }
+
+ private Constant newString(final String value) {
+ key2.set('S', value, null, null);
+ Constant result = get(key2);
+ if (result == null) {
+ newUTF8(value);
+ result = new Constant(key2);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newClass(final String value) {
+ key2.set('C', value, null, null);
+ Constant result = get(key2);
+ if (result == null) {
+ newUTF8(value);
+ result = new Constant(key2);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newMethodType(final String methodDescriptor) {
+ key2.set('t', methodDescriptor, null, null);
+ Constant result = get(key2);
+ if (result == null) {
+ newUTF8(methodDescriptor);
+ result = new Constant(key2);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newHandle(final int tag, final String owner,
+ final String name, final String desc, final boolean itf) {
+ key4.set((char) ('h' + tag - 1 + (itf && tag != Opcodes.H_INVOKEINTERFACE? 4: 0)), owner, name, desc);
+ Constant result = get(key4);
+ if (result == null) {
+ if (tag <= Opcodes.H_PUTSTATIC) {
+ newField(owner, name, desc);
+ } else {
+ newMethod(owner, name, desc, itf);
+ }
+ result = new Constant(key4);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newConst(final Object cst) {
+ if (cst instanceof Integer) {
+ int val = ((Integer) cst).intValue();
+ return newInteger(val);
+ } else if (cst instanceof Float) {
+ float val = ((Float) cst).floatValue();
+ return newFloat(val);
+ } else if (cst instanceof Long) {
+ long val = ((Long) cst).longValue();
+ return newLong(val);
+ } else if (cst instanceof Double) {
+ double val = ((Double) cst).doubleValue();
+ return newDouble(val);
+ } else if (cst instanceof String) {
+ return newString((String) cst);
+ } else if (cst instanceof Type) {
+ Type t = (Type) cst;
+ int s = t.getSort();
+ if (s == Type.OBJECT) {
+ return newClass(t.getInternalName());
+ } else if (s == Type.METHOD) {
+ return newMethodType(t.getDescriptor());
+ } else { // s == primitive type or array
+ return newClass(t.getDescriptor());
+ }
+ } else if (cst instanceof Handle) {
+ Handle h = (Handle) cst;
+ return newHandle(h.getTag(), h.getOwner(), h.getName(), h.getDesc(), h.isInterface());
+ } else {
+ throw new IllegalArgumentException("value " + cst);
+ }
+ }
+
+ public Constant newField(final String owner, final String name,
+ final String desc) {
+ key3.set('G', owner, name, desc);
+ Constant result = get(key3);
+ if (result == null) {
+ newClass(owner);
+ newNameType(name, desc);
+ result = new Constant(key3);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newMethod(final String owner, final String name,
+ final String desc, final boolean itf) {
+ key3.set(itf ? 'N' : 'M', owner, name, desc);
+ Constant result = get(key3);
+ if (result == null) {
+ newClass(owner);
+ newNameType(name, desc);
+ result = new Constant(key3);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newInvokeDynamic(String name, String desc, Handle bsm,
+ Object... bsmArgs) {
+ key5.set(name, desc, bsm, bsmArgs);
+ Constant result = get(key5);
+ if (result == null) {
+ newNameType(name, desc);
+ newHandle(bsm.getTag(), bsm.getOwner(), bsm.getName(),
+ bsm.getDesc(), bsm.isInterface());
+ for (int i = 0; i < bsmArgs.length; i++) {
+ newConst(bsmArgs[i]);
+ }
+ result = new Constant(key5);
+ put(result);
+ }
+ return result;
+ }
+
+ public Constant newNameType(final String name, final String desc) {
+ key2.set('T', name, desc, null);
+ Constant result = get(key2);
+ if (result == null) {
+ newUTF8(name);
+ newUTF8(desc);
+ result = new Constant(key2);
+ put(result);
+ }
+ return result;
+ }
+
+ private Constant get(final Constant key) {
+ return get((Object) key);
+ }
+
+ private void put(final Constant cst) {
+ put(cst, cst);
+ }
+}
\ No newline at end of file
diff --git a/src/java/nginx/clojure/asm/optimizer/FieldConstantsCollector.java b/src/java/nginx/clojure/asm/optimizer/FieldConstantsCollector.java
new file mode 100644
index 00000000..cfb184c3
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/FieldConstantsCollector.java
@@ -0,0 +1,89 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.Attribute;
+import nginx.clojure.asm.FieldVisitor;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
+
+/**
+ * A {@link FieldVisitor} that collects the {@link Constant}s of the fields it
+ * visits.
+ *
+ * @author Eric Bruneton
+ */
+public class FieldConstantsCollector extends FieldVisitor {
+
+ private final ConstantPool cp;
+
+ public FieldConstantsCollector(final FieldVisitor fv, final ConstantPool cp) {
+ super(Opcodes.ASM5, fv);
+ this.cp = cp;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(final String desc,
+ final boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleAnnotations");
+ }
+ return new AnnotationConstantsCollector(fv.visitAnnotation(desc,
+ visible), cp);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleTypeAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleTypeAnnotations");
+ }
+ return new AnnotationConstantsCollector(fv.visitAnnotation(desc,
+ visible), cp);
+ }
+
+ @Override
+ public void visitAttribute(final Attribute attr) {
+ // can do nothing
+ fv.visitAttribute(attr);
+ }
+
+ @Override
+ public void visitEnd() {
+ fv.visitEnd();
+ }
+}
diff --git a/src/java/nginx/clojure/asm/optimizer/JarOptimizer.java b/src/java/nginx/clojure/asm/optimizer/JarOptimizer.java
new file mode 100644
index 00000000..2be7b358
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/JarOptimizer.java
@@ -0,0 +1,235 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import nginx.clojure.asm.ClassReader;
+import nginx.clojure.asm.ClassVisitor;
+import nginx.clojure.asm.FieldVisitor;
+import nginx.clojure.asm.MethodVisitor;
+import nginx.clojure.asm.Opcodes;
+
+/**
+ * A Jar file optimizer.
+ *
+ * @author Eric Bruneton
+ */
+public class JarOptimizer {
+
+ static final Set API = new HashSet();
+ static final Map HIERARCHY = new HashMap();
+ static boolean nodebug = false;
+
+ public static void main(final String[] args) throws IOException {
+ File f = new File(args[0]);
+ InputStream is = new GZIPInputStream(new FileInputStream(f));
+ BufferedReader lnr = new LineNumberReader(new InputStreamReader(is));
+ while (true) {
+ String line = lnr.readLine();
+ if (line != null) {
+ if (line.startsWith("class")) {
+ String c = line.substring(6, line.lastIndexOf(' '));
+ String sc = line.substring(line.lastIndexOf(' ') + 1);
+ HIERARCHY.put(c, sc);
+ } else {
+ API.add(line);
+ }
+ } else {
+ break;
+ }
+ }
+
+ int argIndex = 1;
+ if (args[argIndex].equals("-nodebug")) {
+ nodebug = true;
+ argIndex++;
+ }
+
+ optimize(new File(args[argIndex]));
+ }
+
+ static void optimize(final File f) throws IOException {
+ if (nodebug && f.getName().contains("debug")) {
+ return;
+ }
+
+ if (f.isDirectory()) {
+ File[] files = f.listFiles();
+ for (int i = 0; i < files.length; ++i) {
+ optimize(files[i]);
+ }
+ } else if (f.getName().endsWith(".jar")) {
+ File g = new File(f.getParentFile(), f.getName() + ".new");
+ ZipFile zf = new ZipFile(f);
+ ZipOutputStream out = new ZipOutputStream(new FileOutputStream(g));
+ Enumeration extends ZipEntry> e = zf.entries();
+ byte[] buf = new byte[10000];
+ while (e.hasMoreElements()) {
+ ZipEntry ze = e.nextElement();
+ if (ze.isDirectory()) {
+ out.putNextEntry(ze);
+ continue;
+ }
+ out.putNextEntry(ze);
+ if (ze.getName().endsWith(".class")) {
+ ClassReader cr = new ClassReader(zf.getInputStream(ze));
+ // cr.accept(new ClassDump(), 0);
+ cr.accept(new ClassVerifier(), 0);
+ }
+ InputStream is = zf.getInputStream(ze);
+ int n;
+ do {
+ n = is.read(buf, 0, buf.length);
+ if (n != -1) {
+ out.write(buf, 0, n);
+ }
+ } while (n != -1);
+ out.closeEntry();
+ }
+ out.close();
+ zf.close();
+ if (!f.delete()) {
+ throw new IOException("Cannot delete file " + f);
+ }
+ if (!g.renameTo(f)) {
+ throw new IOException("Cannot rename file " + g);
+ }
+ }
+ }
+
+ static class ClassDump extends ClassVisitor {
+
+ String owner;
+
+ public ClassDump() {
+ super(Opcodes.ASM5);
+ }
+
+ @Override
+ public void visit(final int version, final int access,
+ final String name, final String signature,
+ final String superName, final String[] interfaces) {
+ owner = name;
+ if (owner.startsWith("java/")) {
+ System.out.println("class " + name + ' ' + superName);
+ }
+ }
+
+ @Override
+ public FieldVisitor visitField(final int access, final String name,
+ final String desc, final String signature, final Object value) {
+ if (owner.startsWith("java/")) {
+ System.out.println(owner + ' ' + name);
+ }
+ return null;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name,
+ final String desc, final String signature,
+ final String[] exceptions) {
+ if (owner.startsWith("java/")) {
+ System.out.println(owner + ' ' + name + desc);
+ }
+ return null;
+ }
+ }
+
+ static class ClassVerifier extends ClassVisitor {
+
+ String owner;
+
+ String method;
+
+ public ClassVerifier() {
+ super(Opcodes.ASM5);
+ }
+
+ @Override
+ public void visit(final int version, final int access,
+ final String name, final String signature,
+ final String superName, final String[] interfaces) {
+ owner = name;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name,
+ final String desc, final String signature,
+ final String[] exceptions) {
+ method = name + desc;
+ return new MethodVisitor(Opcodes.ASM5) {
+ @Override
+ public void visitFieldInsn(final int opcode,
+ final String owner, final String name, final String desc) {
+ check(owner, name);
+ }
+
+ @Override
+ public void visitMethodInsn(final int opcode,
+ final String owner, final String name,
+ final String desc, final boolean itf) {
+ check(owner, name + desc);
+ }
+ };
+ }
+
+ void check(String owner, String member) {
+ if (owner.startsWith("java/")) {
+ String o = owner;
+ while (o != null) {
+ if (API.contains(o + ' ' + member)) {
+ return;
+ }
+ o = HIERARCHY.get(o);
+ }
+ System.out.println("WARNING: " + owner + ' ' + member
+ + " called in " + this.owner + ' ' + method
+ + " is not defined in JDK 1.3 API");
+ }
+ }
+ }
+}
diff --git a/src/java/nginx/clojure/asm/optimizer/MethodConstantsCollector.java b/src/java/nginx/clojure/asm/optimizer/MethodConstantsCollector.java
new file mode 100644
index 00000000..76aef7a4
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/MethodConstantsCollector.java
@@ -0,0 +1,224 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.Handle;
+import nginx.clojure.asm.Label;
+import nginx.clojure.asm.MethodVisitor;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.TypePath;
+
+/**
+ * An {@link MethodVisitor} that collects the {@link Constant}s of the methods
+ * it visits.
+ *
+ * @author Eric Bruneton
+ */
+public class MethodConstantsCollector extends MethodVisitor {
+
+ private final ConstantPool cp;
+
+ public MethodConstantsCollector(final MethodVisitor mv,
+ final ConstantPool cp) {
+ super(Opcodes.ASM5, mv);
+ this.cp = cp;
+ }
+
+ @Override
+ public void visitParameter(String name, int access) {
+ cp.newUTF8("MethodParameters");
+ if (name != null) {
+ cp.newUTF8(name);
+ }
+ mv.visitParameter(name, access);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotationDefault() {
+ cp.newUTF8("AnnotationDefault");
+ return new AnnotationConstantsCollector(mv.visitAnnotationDefault(), cp);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(final String desc,
+ final boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleAnnotations");
+ }
+ return new AnnotationConstantsCollector(mv.visitAnnotation(desc,
+ visible), cp);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleTypeAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleTypeAnnotations");
+ }
+ return new AnnotationConstantsCollector(mv.visitAnnotation(desc,
+ visible), cp);
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(final int parameter,
+ final String desc, final boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleParameterAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleParameterAnnotations");
+ }
+ return new AnnotationConstantsCollector(mv.visitParameterAnnotation(
+ parameter, desc, visible), cp);
+ }
+
+ @Override
+ public void visitTypeInsn(final int opcode, final String type) {
+ cp.newClass(type);
+ mv.visitTypeInsn(opcode, type);
+ }
+
+ @Override
+ public void visitFieldInsn(final int opcode, final String owner,
+ final String name, final String desc) {
+ cp.newField(owner, name, desc);
+ mv.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ @Override
+ public void visitMethodInsn(final int opcode, final String owner,
+ final String name, final String desc, final boolean itf) {
+ cp.newMethod(owner, name, desc, itf);
+ mv.visitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+ Object... bsmArgs) {
+ cp.newInvokeDynamic(name, desc, bsm, bsmArgs);
+ mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+ }
+
+ @Override
+ public void visitLdcInsn(final Object cst) {
+ cp.newConst(cst);
+ mv.visitLdcInsn(cst);
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ cp.newClass(desc);
+ mv.visitMultiANewArrayInsn(desc, dims);
+ }
+
+ @Override
+ public AnnotationVisitor visitInsnAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleTypeAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleTypeAnnotations");
+ }
+ return new AnnotationConstantsCollector(mv.visitInsnAnnotation(typeRef,
+ typePath, desc, visible), cp);
+ }
+
+ @Override
+ public void visitTryCatchBlock(final Label start, final Label end,
+ final Label handler, final String type) {
+ if (type != null) {
+ cp.newClass(type);
+ }
+ mv.visitTryCatchBlock(start, end, handler, type);
+ }
+
+ @Override
+ public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleTypeAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleTypeAnnotations");
+ }
+ return new AnnotationConstantsCollector(mv.visitTryCatchAnnotation(
+ typeRef, typePath, desc, visible), cp);
+ }
+
+ @Override
+ public void visitLocalVariable(final String name, final String desc,
+ final String signature, final Label start, final Label end,
+ final int index) {
+ if (signature != null) {
+ cp.newUTF8("LocalVariableTypeTable");
+ cp.newUTF8(name);
+ cp.newUTF8(signature);
+ }
+ cp.newUTF8("LocalVariableTable");
+ cp.newUTF8(name);
+ cp.newUTF8(desc);
+ mv.visitLocalVariable(name, desc, signature, start, end, index);
+ }
+
+ @Override
+ public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
+ TypePath typePath, Label[] start, Label[] end, int[] index,
+ String desc, boolean visible) {
+ cp.newUTF8(desc);
+ if (visible) {
+ cp.newUTF8("RuntimeVisibleTypeAnnotations");
+ } else {
+ cp.newUTF8("RuntimeInvisibleTypeAnnotations");
+ }
+ return new AnnotationConstantsCollector(
+ mv.visitLocalVariableAnnotation(typeRef, typePath, start, end,
+ index, desc, visible), cp);
+ }
+
+ @Override
+ public void visitLineNumber(final int line, final Label start) {
+ cp.newUTF8("LineNumberTable");
+ mv.visitLineNumber(line, start);
+ }
+
+ @Override
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ cp.newUTF8("Code");
+ mv.visitMaxs(maxStack, maxLocals);
+ }
+}
diff --git a/src/java/nginx/clojure/asm/optimizer/MethodOptimizer.java b/src/java/nginx/clojure/asm/optimizer/MethodOptimizer.java
new file mode 100644
index 00000000..397d3d23
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/MethodOptimizer.java
@@ -0,0 +1,180 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import java.util.HashMap;
+
+import nginx.clojure.asm.AnnotationVisitor;
+import nginx.clojure.asm.Attribute;
+import nginx.clojure.asm.FieldVisitor;
+import nginx.clojure.asm.Label;
+import nginx.clojure.asm.MethodVisitor;
+import nginx.clojure.asm.Opcodes;
+import nginx.clojure.asm.Type;
+import nginx.clojure.asm.TypePath;
+import nginx.clojure.asm.commons.MethodRemapper;
+import nginx.clojure.asm.commons.Remapper;
+
+/**
+ * A {@link MethodVisitor} that renames fields and methods, and removes debug
+ * info.
+ *
+ * @author Eugene Kuleshov
+ */
+public class MethodOptimizer extends MethodRemapper implements Opcodes {
+
+ private final ClassOptimizer classOptimizer;
+
+ public MethodOptimizer(ClassOptimizer classOptimizer, MethodVisitor mv,
+ Remapper remapper) {
+ super(Opcodes.ASM5, mv, remapper);
+ this.classOptimizer = classOptimizer;
+ }
+
+ // ------------------------------------------------------------------------
+ // Overridden methods
+ // ------------------------------------------------------------------------
+
+ @Override
+ public void visitParameter(String name, int access) {
+ // remove parameter info
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotationDefault() {
+ // remove annotations
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ // remove annotations
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String desc, boolean visible) {
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(final int parameter,
+ final String desc, final boolean visible) {
+ // remove annotations
+ return null;
+ }
+
+ @Override
+ public void visitLocalVariable(final String name, final String desc,
+ final String signature, final Label start, final Label end,
+ final int index) {
+ // remove debug info
+ }
+
+ @Override
+ public void visitLineNumber(final int line, final Label start) {
+ // remove debug info
+ }
+
+ @Override
+ public void visitFrame(int type, int local, Object[] local2, int stack,
+ Object[] stack2) {
+ // remove frame info
+ }
+
+ @Override
+ public void visitAttribute(Attribute attr) {
+ // remove non standard attributes
+ }
+
+ @Override
+ public void visitLdcInsn(Object cst) {
+ if (!(cst instanceof Type)) {
+ super.visitLdcInsn(cst);
+ return;
+ }
+
+ // transform Foo.class for 1.2 compatibility
+ String ldcName = ((Type) cst).getInternalName();
+ String fieldName = "class$" + ldcName.replace('/', '$');
+ if (!classOptimizer.syntheticClassFields.contains(ldcName)) {
+ classOptimizer.syntheticClassFields.add(ldcName);
+ FieldVisitor fv = classOptimizer.syntheticFieldVisitor(ACC_STATIC
+ | ACC_SYNTHETIC, fieldName, "Ljava/lang/Class;");
+ fv.visitEnd();
+ }
+
+ String clsName = classOptimizer.clsName;
+ mv.visitFieldInsn(GETSTATIC, clsName, fieldName, "Ljava/lang/Class;");
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name,
+ String desc, boolean itf) {
+ // rewrite boxing method call to use constructor to keep 1.3/1.4
+ // compatibility
+ String[] constructorParams;
+ if (opcode == INVOKESTATIC && name.equals("valueOf")
+ && (constructorParams = BOXING_MAP.get(owner + desc)) != null) {
+ String type = constructorParams[0];
+ String initDesc = constructorParams[1];
+ super.visitTypeInsn(NEW, type);
+ super.visitInsn(DUP);
+ super.visitInsn((initDesc == "(J)V" || initDesc == "(D)V") ? DUP2_X2
+ : DUP2_X1);
+ super.visitInsn(POP2);
+ super.visitMethodInsn(INVOKESPECIAL, type, "", initDesc,
+ false);
+ return;
+ }
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ private static final HashMap BOXING_MAP;
+ static {
+ String[][] boxingNames = {
+ // Boolean.valueOf is 1.4 and is used by the xml package, so no
+ // rewrite
+ { "java/lang/Byte", "(B)V" }, { "java/lang/Short", "(S)V" },
+ { "java/lang/Character", "(C)V" },
+ { "java/lang/Integer", "(I)V" }, { "java/lang/Long", "(J)V" },
+ { "java/lang/Float", "(F)V" }, { "java/lang/Double", "(D)V" }, };
+ HashMap map = new HashMap();
+ for (String[] boxingName : boxingNames) {
+ String wrapper = boxingName[0];
+ String desc = boxingName[1];
+ String boxingMethod = wrapper + '(' + desc.charAt(1) + ")L"
+ + wrapper + ';';
+ map.put(boxingMethod, boxingName);
+ }
+ BOXING_MAP = map;
+ }
+}
diff --git a/src/java/nginx/clojure/asm/optimizer/NameMapping.java b/src/java/nginx/clojure/asm/optimizer/NameMapping.java
new file mode 100644
index 00000000..f01cd07d
--- /dev/null
+++ b/src/java/nginx/clojure/asm/optimizer/NameMapping.java
@@ -0,0 +1,114 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package nginx.clojure.asm.optimizer;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import nginx.clojure.asm.Type;
+
+/**
+ * A MAPPING from names to names, used to rename classes, fields and methods.
+ *
+ * @author Eric Bruneton
+ */
+public class NameMapping {
+
+ public final Properties mapping;
+
+ public final Set