From dee6153e11e6f5cd8f6ba4983b622eb0c617c801 Mon Sep 17 00:00:00 2001 From: xfeep Date: Thu, 10 Aug 2017 23:23:16 +0800 Subject: [PATCH 1/6] init jersey example --- example-projects/jersey-example/.classpath | 26 ++++++ example-projects/jersey-example/.gitignore | 1 + example-projects/jersey-example/.project | 23 +++++ .../org.eclipse.core.resources.prefs | 4 + .../.settings/org.eclipse.jdt.core.prefs | 5 ++ .../.settings/org.eclipse.m2e.core.prefs | 4 + example-projects/jersey-example/README.md | 7 ++ example-projects/jersey-example/pom.xml | 88 +++++++++++++++++++ .../nginx/clojure/jersey/example/Main.java | 45 ++++++++++ .../clojure/jersey/example/MyResource.java | 32 +++++++ .../jersey/example/MyResourceTest.java | 48 ++++++++++ 11 files changed, 283 insertions(+) create mode 100644 example-projects/jersey-example/.classpath create mode 100644 example-projects/jersey-example/.gitignore create mode 100644 example-projects/jersey-example/.project create mode 100644 example-projects/jersey-example/.settings/org.eclipse.core.resources.prefs create mode 100644 example-projects/jersey-example/.settings/org.eclipse.jdt.core.prefs create mode 100644 example-projects/jersey-example/.settings/org.eclipse.m2e.core.prefs create mode 100644 example-projects/jersey-example/README.md create mode 100644 example-projects/jersey-example/pom.xml create mode 100644 example-projects/jersey-example/src/main/java/nginx/clojure/jersey/example/Main.java create mode 100644 example-projects/jersey-example/src/main/java/nginx/clojure/jersey/example/MyResource.java create mode 100644 example-projects/jersey-example/src/test/java/nginx/clojure/jersey/example/MyResourceTest.java 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); + } +} From a17b6fd3dcb617760f1eab5cd4c0336a183a2058 Mon Sep 17 00:00:00 2001 From: xfeep Date: Thu, 10 Aug 2017 23:23:42 +0800 Subject: [PATCH 2/6] modify test files --- nginx-clojure-embed/configure-centos5-x64 | 139 ++ nginx-clojure-embed/configure-win32 | 147 ++ nginx-clojure-embed/macos-issue | 11 + test/nginx-working-dir/conf/nginx-jersey.conf | 23 +- test/nginx-working-dir/conf/nginx-plain.conf | 2 +- test/nginx-working-dir/testfiles/large.txt | 1779 +++++++++++++++++ 6 files changed, 2095 insertions(+), 6 deletions(-) create mode 100755 nginx-clojure-embed/configure-centos5-x64 create mode 100644 nginx-clojure-embed/configure-win32 create mode 100644 nginx-clojure-embed/macos-issue create mode 100644 test/nginx-working-dir/testfiles/large.txt 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/test/nginx-working-dir/conf/nginx-jersey.conf b/test/nginx-working-dir/conf/nginx-jersey.conf index 53090209..89b4b4c1 100644 --- a/test/nginx-working-dir/conf/nginx-jersey.conf +++ b/test/nginx-working-dir/conf/nginx-jersey.conf @@ -11,8 +11,8 @@ master_process off; worker_processes 1; -error_log logs/error.log; -#error_log logs/error.log debug; +#error_log logs/error.log; +error_log logs/error.log debug; #error_log logs/error.log notice; #error_log logs/error.log info; @@ -53,7 +53,7 @@ http { jvm_var ncdev '/home/who/git/nginx-clojure'; jvm_var mrr '/home/who/.m2/repository'; - jvm_var ncjar '#{ncdev}/target/nginx-clojure-0.4.4.jar'; + jvm_var ncjar '#{ncdev}/target/nginx-clojure-0.4.5.jar'; ###run tool mode , 't' means Tool @@ -101,7 +101,7 @@ http { server { - listen 8080; + listen 8087; server_name localhost; #uncomment this two lines for performance test @@ -125,7 +125,20 @@ http { gzip on; gzip_types application/javascript application/xml text/plain text/css 'text/html;charset=UTF-8'; } - + + + location /jersey-example { + content_handler_name 'nginx.clojure.bridge.NginxBridgeHandler'; + #content_handler_property bridge.lib.dirs '/home/who/git/jersey/examples/json-jackson/target/dependency'; + content_handler_property bridge.lib.cp '/home/who/git/nginx-clojure/nginx-jersey/bin:/home/who/git/nginx-clojure/example-projects/jersey-example/target/classes:/home/who/git/nginx-clojure/example-projects/jersey-example/target/jersey-example-1.0-jar-with-dependencies.jar'; + content_handler_property bridge.imp 'nginx.clojure.jersey.NginxJerseyContainer'; + content_handler_property jersey.app.path '/jersey-example'; + content_handler_property jersey.app.resources ' + nginx.clojure.jersey.example.MyResource + '; + gzip on; + gzip_types application/javascript application/xml text/plain text/css 'text/html;charset=UTF-8'; + } location /dump { handler_type 'java'; diff --git a/test/nginx-working-dir/conf/nginx-plain.conf b/test/nginx-working-dir/conf/nginx-plain.conf index 4f57c837..fdd93b0e 100644 --- a/test/nginx-working-dir/conf/nginx-plain.conf +++ b/test/nginx-working-dir/conf/nginx-plain.conf @@ -8,7 +8,7 @@ daemon off; #user nobody; ###you can set worker_processes =1 for easy debug ###if master_process is off, the count of worker processes will be 1 regardless of worker_processes settings -worker_processes 4; +worker_processes 1; error_log logs/error.log; diff --git a/test/nginx-working-dir/testfiles/large.txt b/test/nginx-working-dir/testfiles/large.txt new file mode 100644 index 00000000..422109ce --- /dev/null +++ b/test/nginx-working-dir/testfiles/large.txt @@ -0,0 +1,1779 @@ +Release 4.3.3 +------------------- + +HttpClient 4.3.3 (GA) is a bug fix release that fixes a regression introduced by the previous +release causing a significant performance degradation in compressed content processing. + +Users of HttpClient 4.3 are encouraged to upgrade. + +Changelog: +------------------- + +* [HTTPCLIENT-1466] FileBodyPart#generateContentType() ignores custom ContentType values. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1453] Thread safety regression in PoolingHttpClientConnectionManager + #closeExpiredConnections that can lead to ConcurrentModificationException. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1461] fixed performance degradation in compressed content processing + introduced by HTTPCLIENT-1432. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1457] Incorrect handling of Windows (NT) credentials by + SystemDefaultCredentialsProvider. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1456] Request retrial after status 503 causes ClientProtocolException. + Contributed by Oleg Kalnichevski + + + +Release 4.3.2 +------------------- + +HttpClient 4.3.2 (GA) is a maintenance release that delivers a number of improvements +as well as bug fixes for issues reported since 4.3.1 release. SNI support for +Oracle JRE 1.7+ is being among the most notable improvements. + +Users of HttpClient 4.3 are encouraged to upgrade. + +Changelog: +------------------- + +* [HTTPCLIENT-1447] Clients created with HttpClients.createMinimal do not work with absolute URIs + Contributed by Joseph Walton + +* [HTTPCLIENT-1446] NTLM proxy + BASIC target auth fails with 'Unexpected state: + MSG_TYPE3_GENERATED'. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1443] HttpCache uses the physical host instead of the virtual host as a cache key. + Contributed by Francois-Xavier Bonnet + +* [HTTPCLIENT-1442] Authentication header set by the user gets removed in case + of proxy authentication (affects plan HTTP requests only). + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1441] Caching AsynchronousValidationRequest leaks connections. + Contributed by Dominic Tootell + +* [HTTPCLIENT-1440] 'file' scheme in redirect location URI causes NPE. + Contributed by James Leigh + +* [HTTPCLIENT-1437] Made Executor#execute thread safe. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1119] SNI support (Oracle Java 1.7+ only). + Contributed by Bruno Harbulot + +* [HTTPCLIENT-1435] Fluent Executor ignores custom request properties. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1432] Lazy decompressing of HttpEntity#getContent() to avoid EOFException + in case of an empty response with 'Content-Encoding: gzip' header. + Contributed by Yihua Huang + +* [HTTPCLIENT-1431] (Regression) deprecated connection manager cannot be used with + a custom LayeredSchemeSocketFactory. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1425] Fixed socket closed exception thrown by caching HttpClient when the origin + server sends a long chunked response. + Contributed by James Leigh + +* [HTTPCLIENT-1417] Fixed NPE in BrowserCompatSpec#formatCookies caused by version 1 + cookies with null cookie value. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1416] Fixed NPE in CachingHttpClientBuilder#build(). + Contributed by Oleg Kalnichevski + + + +Release 4.3.1 +------------------- + +HttpClient 4.3.1 (GA) is a bug fix release that addresses a number of issues reported since +release 4.3. + +Users of HttpClient 4.3 are strongly encouraged to upgrade. + +Changelog +------------------- + +* [HTTPCLIENT-1410] Browser compatible hostname verifier no longer rejects + *.co., *.gov., *.info., etc as invalid. + Contributed by Oleg Kalnichevski + +* Ensure X509HostnameVerifier is never null. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1405] CONNECT HTTP/1.1 requests lack mandatory 'Host' header. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1402] Cache default User-Agent value. + Contributed by yuexiaojun + +* [HTTPCLIENT-1398] Fixed invalid OSGi metadata caused by corrupted Maven bundle plugin metadata. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1399] Fixed NPE in RequestBuilder. + Contributed by Oleg Kalnichevski + + + + +Release 4.3 Final +------------------- + +This is the first stable (GA) release of HttpClient 4.3. The most notable enhancements included +in this release are: + +* Support for Java 7 try-with-resources for resource management (connection release.) + +* Added fluent Builder classes for HttpEntity, HttpRequest, HttpClient and SSLContext instances. + +* Deprecation of preference and configuration API based on HttpParams interface in favor of +constructor injection and plain configuration objects. + +* Reliance on object immutability instead of access synchronization for thread safety. +Several old classes whose instances can be shared by multiple request exchanges have +been replaced by immutable equivalents. + +* DefaultHttpClient, DecompressingHttpClient, CachingHttpClient and similar classes are +deprecated in favor of builder classes that produce immutable HttpClient instances. + +* HttpClient builders now dynamically construct a request execution pipeline tailored +specifically to the user configuration by physically excluding unnecessary protocol components. + +* There is now an option to construct a minimal HttpClient implementation that can only execute +basic HTTP message exchanges without redirects, authentication, state management or proxy support. +This feature might be of particular use in web crawler development. + +* There is now option to avoid strict URI syntax for request URIs by executing HTTP requests +with an explicitly specified target host. HttpClient will no longer attempt to parse the request +URI if it does not need to extract the target host from it. + +This release also includes all fixes from the stable 4.2.x release branch. + + +Changelog +------------------- +* [HTTPCLIENT-1371] Weak ETag Validation is Useful On PUT With If-Match + Contributed by James Leigh + +* [HTTPCLIENT-1394] Support for Native windows Negotiate/NTLM via JNA + Contributed by Ryan McKinley + +* [HTTPCLIENT-1384] Expose CacheInvalidator interface. + Contributed by Nicolas Richeton + +* [HTTPCLIENT-1385] Fixed path normalization in CacheKeyGenerator + Contributed by James Leigh + +* [HTTPCLIENT-1370] Response to non-GET requests should never be cached with the default + ResponseCachingPolicy + Contributed by James Leigh + +* [HTTPCLIENT-1373] OPTIONS and TRACE should not invalidate cache + Contributed by James Leigh + +* [HTTPCLIENT-1383] HttpClient enters an infinite loop during NTLM authentication if the opposite + endpoint keeps responding with a type 2 NTLM response after type 3 MTLM message has already been + sent by the client. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1372] Refactor HttpMultipart, and add RFC6532 mode, so that headers in post + are no longer constrained to ASCII values. + Contributed by Karl Wright + +* [HTTPCLIENT-1377] User principal for non-NTLM authentication is incorrectly generated when using + user credentials are specified as NTCredentials + Contributed by Gary Gregory + + + +Release 4.3 BETA2 +------------------- + +This is the second BETA release of HttpClient 4.3. The most notable features and improvements +in the 4.3 branch are: Support for Java 7 try-with-resources for resource management (connection +release); fluent Builder classes for HttpEntity, HttpRequest and HttpClient instances, deprecation +of preference and configuration API based on HttpParams interface in favor of constructor injection +and plain configuration objects, reliance on object immutability instead of access synchronization +for thread safety. + +This release also includes all fixes from the stable 4.2.x release branch. + +Changelog +------------------- + + +* [HTTPCLIENT-1366] org.apache.http.client.utils.URLEncodedUtils should parse the semicolon as a query parameter separator. + Contributed by Gary Gregory + +* [HTTPCLIENT-1365] NPE when ManagedHttpClientConnectionFactory.create(ConnectionConfig) is called with null. + Contributed by Gary Gregory + +* [HTTPCLIENT-1362] Better error messages for connect timed out and connection refused + exceptions. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1360] separate out DeflateInputStream as an independent class, + so it can be used by others. + Contributed by Karl Wright + +* [HTTPCLIENT-1359] repeated requests using the same context fail if they redirect. + Contributed by James Leigh + +* [HTTPCLIENT-1354] do not quote algorithm parameter in DIGEST auth response. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1351] Added utility method to resolve final location from original request, + target host and a list of redirects. + Contributed by James Leigh + +* [HTTPCLIENT-1344] Userinfo credentials in URI should not default to preemptive BASIC + authentication. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1345] Useinfo credentials ignored in redirect location header. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1294] HttpClient to rewrite host name of the redirect location URI in order + to avoid circular redirect exception due to host name case mismatch. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1264] Add support for multiple levels of browser compatibility + to BrowserCompatSpec and BrowserCompatSpecFactory. Include constructor + argument for IE medium-security compatibility. + Contributed by Karl Wright (kwright at apache.org) + +* [HTTPCLIENT-1349] SSLSocketFactory incorrectly identifies key passed with keystore as + the keystore password. + Contributed by David Graff + +* [HTTPCLIENT-1346] Ensure propagation of SSL handshake exceptions. + Contributed by Pasi Eronen + +* [HTTPCLIENT-1343] SSLSocketFactory optional parameters for supported SSL protocols and cipher + suites. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1238] Contribute Bundle Activator And Central Proxy Configuration. + Contributed by Simone Tripodi + +* [HTTPCLIENT-1299] (regression) cache incorrectly disposes of the underlying cache resource + when storing variant entry. + Contributed by James Leigh + +* [HTTPCLIENT-1342] Redirects with underscore character in the location hostname cause + "java.lang.IllegalArgumentException: Host name may not be null". + Contributed by Oleg Kalnichevski + + + +Release 4.3 BETA1 +------------------- + +This is the first BETA release of HttpClient 4.3. The 4.3 branch enhances HttpClient in several +key areas and includes several notable features and improvements: Support for Java 7 +try-with-resources for resource management (connection release); fluent Builder classes for +HttpEntity, HttpRequest and HttpClient instances, deprecation of preference and configuration API +based on HttpParams interface in favor of constructor injection and plain configuration objects, +reliance on object immutability instead of access synchronization for thread safety. + +This release also includes all fixes from the stable 4.2.x release branch. + + +Changelog +------------------- + +* [HTTPCLIENT-1317] InetAddressUtils should handle IPv6 Addresses with Embedded IPv4 Addresses + Contributed Sebastian Bazley . + +* [HTTPCLIENT-1320] Leverage javax.net.ssl.SSLSocketFactory#getDefault() to initialize SSL context + based on system defaults instead of using an internal custom routine. + Contributed by Abe Backus and Oleg Kalnichevski + +* [HTTPCLIENT-1316] Certificate verification rejects IPv6 addresses which are not String-equal. + Contributed Sebastian Bazley . + +* [HTTPCLIENT-1307] Future based asynchronous request execution. + Contributed by Jilles van Gurp + +* [HTTPCLIENT-1313] Fixed IllegalStateException in deprecated ThreadSafeClientConnManager. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1298] Add AsynchronousValidator in HttpClientBuilder's list of closeable objects. + Contributed by Martin Meinhold + + + +Release 4.3 ALPHA1 +------------------- + +This is the first ALPHA release of HttpClient 4.3. The 4.3 branch enhances HttpClient in several +key areas and includes several notable features and improvements: Support for Java 7 +try-with-resources for resource management (connection release); fluent Builder classes for +HttpEntity, HttpRequest and HttpClient instances, deprecation of preference and configuration API +based on HttpParams interface in favor of constructor injection and plain configuration objects, +reliance on object immutability instead of access synchronization for thread safety. + +We are kindly asking all upstream projects to review API changes and help us improve +the APIs by providing feedback and sharing ideas on dev@hc.apache.org. + +This release also includes all fixes from the stable 4.2.x release branch. + +Please note that new features included in this release are still considered experimental and +their API may change in the future 4.3 alpha and beta releases. + + +Changelog +------------------- + +* [HTTPCLIENT-1250] Allow query string to be ignored when determining cacheability for + HTTP 1.0 responses. + Contributed by Don Brown + +* [HTTPCLIENT-1261] Make SystemDefaultHttpClient honor http.agent system property. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-900] Don't enforce URI syntax for messages with an explicit target host. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1190] HttpClient cache does not support "Vary: Cookie" + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1259] Calling #abort() on requests executed with DecompressingHttpClient has no + effect. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1253] URIBuilder setParameter() method could exceed the HTTP header size. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1216] Added method to force clean thread-local used by DateUtils. + Contributed by Oleg Kalnichevski + + +Release 4.2.3 +------------------- + +HttpClient 4.2.3 (GA) is a bug fix release that addresses a number of issues reported since +release 4.2.2. This release also includes a thoroughly reworked NTLM authentication engine +which should result in a better compatibility with the newest Microsoft products. + +Users of HttpClient 4.x are advised to upgrade. + +Changelog +------------------- + +* [HTTPCLIENT-1296] NPE gets thrown if you combine a default host with a virtual host + that has a -1 value for the port. + Contributed by Karl Wright + +* [HTTPCLIENT-1290] 304 cached response never reused with If-modified-since conditional + requests. + Contributed by Francois-Xavier Bonnet + +* [HTTPCLIENT-1291] Absolute request URIs without an explicitly specified path are rewritten + to have "/" path). + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1286] Request URI rewriting is inconsistent - URI fragments are not removed + from absolute request URIs. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1284] HttpClient incorrectly generates Host header when physical connection + route differs from the host name specified in the request URI. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1293] Kerberos and SPNego auth schemes use incorrect authorization header name + when authenticating with a proxy. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1283] NTLM needs to use Locale-independent form of + toUpperCase(). + Contributed by Karl Wright + +* [HTTPCLIENT-1279] Target host responding with status 407 (proxy authentication required) + causes an NPE. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1281] GzipDecompressingEntity does not release InputStream when an IOException + occurs while reading the Gzip header + Contributed by Francois-Xavier Bonnet + +* [HTTPCLIENT-1277] Caching client sends a 304 to an unconditional request. + Contributed by Francois-Xavier Bonnet + +* [HTTPCLIENT-1278] Update NTLM documentation. + Contributed by Karl Wright + +* SystemDefaultHttpClient misinterprets 'http.keepAlive' default value and disables + connection persistence if the system property is not set. This causes connection + based authentication schemes such as NTLM to fail. + +* [HTTPCLIENT-1276] cache update on a 304 response causes NPE. + Contributed by Francois-Xavier Bonnet + +* [HTTPCLIENT-1273] DecompressingHttpClient does not automatically consume response + content in case of an i/o, HTTP or runtime exception thrown by the decompressing + protocol interceptor leading to a potential connection leak. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1268] NTLM engine refactor fix, to correct a buffer overrun, and get NTLMv2 + flags right. + Contributed by Karl Wright + +* [HTTPCLIENT-1266] NTLM engine refactoring and compatibility improvements. + Contributed by Karl Wright + +* [HTTPCLIENT-1263] BrowserCompatSpec: attribute values containing spaces or special characters + should be enclosed with quotes marks for version 1 cookies. + Contributed by Francois-Xavier Bonnet + +* [HTTPCLIENT-1263] CachingHttpClient fails to release connections back to the connection + manager for some type of HTTP response messages when used together with DecompressingHttpClient. + Contributed by Francois-Xavier Bonnet + +* [HTTPCLIENT-1258] Fixed NullPointerException in NTLMEngineImpl caused by null NT domain + attribute. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1254] Redirect with underscore in hostname causes ProtocolException. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1255] AbstractVerifier incorrectly parses certificate CN containing wildcard. + Contributed by Oleg Kalnichevski + + + +Release 4.2.2 +------------------- + +HttpClient 4.2.2 (GA) is a bug fix release that addresses a number of issues reported since +release 4.2.1. + +Users of HttpClient 4.2 are advised to upgrade. + +Changelog +------------------- + +* [HTTPCLIENT-1248] Default and lax redirect strategies should not convert requests redirected + with 307 status to GET method. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1215] BasicAuthCache does not take default ports into consideration when + looking up cached authentication details by HttpHost key. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1241] (regression) Preemptive BASIC authentication failure should be considered + final and no further attempts to re-authenticate using the same credentials should be made. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1229] Fixed NPE in BasicClientConnectionManager that can be triggered by releasing + connection after the connection manager has already been shut down. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1227] Date parsing in DateUtils made more efficient. + Contributed by Patrick Linskey + +* [HTTPCLIENT-1224] (regression) NTLM auth not retried after a redirect over a non-persistent + connection. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1223] Cache could be more aggressive on cache invalidations + from Content-Location. Contributed by Jon Moore . + Contributed by Jon Moore + +* [HTTPCLIENT-1217] AutoRetryHttpClient does not release connection used by the previous response + when request is retried + Contributed by Oleg Kalnichevski + + + +Release 4.2.1 +------------------- + +HttpClient 4.2.1 (GA) is a bug fix release that addresses a number of issues reported since +release 4.2. + +Users of HttpClient 4.2 are advised to upgrade. + +Changelog +------------------- + +* [HTTPCLIENT-1209] Redirect URIs are now normalized. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1202] ResponseCachingPolicy should honor explicit cache-control + directives for other status codes + Contributed by Jon Moore + +* [HTTPCLIENT-1199] DecompressingHttpClient strips content from entity enclosing requests + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1198] HttpHost is not set in HttpContext in CachingHttpClient. + Contributed by Jon Moore + +* [HTTPCLIENT-1200] DecompressingHttpClient fails to generate correct HttpHost context attribute. + Contributed by Guillaume Castagnino + +* [HTTPCLIENT-1192] URIBuilder encodes query parameters twice. + Contributed by Oleg Kalnichevski and Sebastian Bazley . + +* [HTTPCLIENT-1196] Fixed NPE in UrlEncodedFormEntity constructor thrown if charset is null. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1193] Fixed regression in the route tracking logic of the default connection manager + causing cross-site redirect failures. + Contributed by Oleg Kalnichevski + +Release 4.2 +------------------- + +This is the first stable (GA) release of HttpClient 4.2. The most notable enhancements included +in this release are: + +* New facade API for HttpClient based on the concept of a fluent interface. The fluent API exposes + only the most fundamental functions of HttpClient and is intended for relatively simple use cases + that do not require the full flexibility of HttpClient. However, the fluent API almost fully + relieves the users from having to deal with connection management and resource deallocation. + +* Redesigned and rewritten connection management code. + +* Enhanced HTTP authentication API that enables HttpClient to handle more complex authentication + scenarios. HttpClient 4.2 is now capable of making use of multiple authentication challenges + and retry authentication with a fall-back scheme in case the primary one fails. This can be + important for compatibility with Microsoft products that are often configured to use + SPNEGO/Kerberos as the preferred authentication scheme. + + +Changelog +------------------- + +* [HTTPCLIENT-1187] If a revalidation response is deemed too old CachingHttpClient fails to + consume its content resulting in a connection leak. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1186] State of newly created connections in the connection pool is not always + correctly updated potentially allowing those connections to be leased to users with a different + security context. + Contributed by Ralf Poehlmann + +* [HTTPCLIENT-1179] Upgraded Commons Codec dependency to version 1.6 + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1177] always remove fragments from request URIs + Contributed by Oleg Kalnichevski + +Incompatible changes +-------------------- +[Compared to release version 4.1.3] + +The following fields have been deprecated for some time now and have been deleted: + +org.apache.http.client.params.ClientPNames#CONNECTION_MANAGER_FACTORY +org.apache.http.impl.cookie.BrowserCompatSpec#DATE_PATTERNS + +The following methods have been deprecated for some time now and have been deleted: + +org.apache.http.client.params.ClientParamBean#setConnectionManagerFactory(org.apache.http.conn.ClientConnectionManagerFactory) +org.apache.http.client.protocol.ClientContextConfigurer#setAuthSchemePref(java.util.List) +org.apache.http.entity.mime.content.FileBody#writeTo(java.io.OutputStream, int) +org.apache.http.entity.mime.content.InputStreamBody#writeTo(java.io.OutputStream, int) +org.apache.http.entity.mime.content.StringBody#writeTo(java.io.OutputStream, int) + +The following classes have been deprecated for some while now and have been deleted: + +org.apache.http.impl.conn.tsccm.RefQueueHandler +org.apache.http.impl.conn.tsccm.AbstractConnPool no longer implements interface org.apache.http.impl.conn.tsccm.RefQueueHandler +org.apache.http.impl.conn.tsccm.ConnPoolByRoute no longer implements interface org.apache.http.impl.conn.tsccm.RefQueueHandler +org.apache.http.impl.conn.tsccm.RefQueueWorker + + + +Release 4.2 BETA1 +------------------- + +This is the first BETA release of HttpClient 4.2. This release completes development of several +notable enhancements in HttpClient: + +* New facade API for HttpClient based on the concept of a fluent interface. The fluent API exposes + only the most fundamental functions of HttpClient and is intended for relatively simple use cases + that do not require the full flexibility of HttpClient. However, the fluent API almost fully + relieves the users from having to deal with connection management and resource deallocation. + +* Redesigned and rewritten connection management code. As of release 4.2 HttpClient will be using + pooling connection manager per default. + +* Enhanced HTTP authentication API that enables HttpClient to handle more complex authentication + scenarios. HttpClient 4.2 is now capable of making use of multiple authentication challenges + and retry authentication with a fall-back scheme in case the primary one fails. This can be + important for compatibility with Microsoft products that are often configured to use + SPNEGO/Kerberos as the preferred authentication scheme. + + +Changelog +------------------- + +* [HTTPCLIENT-1164] Compressed entities are not being cached properly. + Contributed by Jon Moore . + +* [HTTPCLIENT-1154] MemcachedHttpCacheStorage should allow client to + specify custom prefix string for keys. + Contributed by Jon Moore . + +* [HTTPCLIENT-1153] MemcachedHttpCacheStorage uses URL as cache key; + shouldn't due to fixed maximum-length memcached keys. + Contributed by Jon Moore . + +* [HTTPCLIENT-1157] MemcachedHttpCacheStroage should throw IOExceptions + instead of RuntimeExceptions. + Contributed by James Miller . + +* [HTTPCLIENT-1152] MemcachedHttpCacheStorage should verify class of + returned object before casting. + Contributed by Rajika Kumarasiri . + +* [HTTPCLIENT-1155] CachingHttpClient fails to ensure that the response content gets fully consumed + when using a ResponseHandler, which can potentially lead to connection leaks. + Contributed by James Miller + +* [HTTPCLIENT-1147] When HttpClient-Cache cannot open cache file, should act like miss. + Contributed by Joe Campbell + +* [HTTPCLIENT-1137] Values for the Via header are cached and reused by httpclient-cache. + Contributed by Alin Vasile + +* [HTTPCLIENT-1142] Infinite loop on NTLM authentication failure. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1143] CachingHttpClient leaks connections with stale-if-error. + Contributed by James Miller + +Release 4.2 ALPHA1 +------------------- + +This is the first ALPHA release of HttpClient 4.2. The 4.2 branch enhances HttpClient in several +key areas and includes several notable features and improvements: + +* New facade API for HttpClient based on the concept of a fluent interface. The fluent API exposes + only the most fundamental functions of HttpClient and is intended for relatively simple use cases + that do not require the full flexibility of HttpClient. However, the fluent API almost fully + relieves the users from having to deal with connection management and resource deallocation. + +* Redesigned and rewritten connection management code. As of release 4.2 HttpClient will be using + pooling connection manager per default. + +* Enhanced HTTP authentication API that enables HttpClient to handle more complex authentication + scenarios. HttpClient 4.2 is now capable of making use of multiple authentication challenges + and retry authentication with a fall-back scheme in case the primary one fails. This can be + important for compatibility with Microsoft products that are often configured to use + SPNEGO/Kerberos as the preferred authentication scheme. + +Please note that new features included in this release are still considered experimental and +their API may change in the future ALPHA releases. + +Changelog +------------------- + +* [HTTPCLIENT-1128] SystemDefaultHttpClient (HttpClient implementation initialized using system + properties). + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1135] RandomAccessFile mode 'w' used by HttpClientCache is not valid. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1131] HttpClient to authenticate preemptively using BASIC scheme if a userinfo + attribute is specified in the request URI. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1134] make BasicResponseHandler consume response content in case of an unsuccessful + result (status code >= 300). + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1132] ProxyClient implementation. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1127] fixed dead-lock between SingleClientConnManager and AbstractPooledConnAdapter. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1107] Auth framework redesign. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1116] ResponseCachingPolicy uses integers for sizes + Contributed by Greg Bowyer + +* [HTTPCLIENT-1123] Support for pluggable DNS resolvers. + Contributed by Alin Vasile + +* [HTTPCLIENT-1120] DefaultHttpRequestRetryHandler#retryRequest should not retry aborted requests. + Contributed by Alin Vasile + +* Support for auth-int qop (quality of protection) option in Digest auth scheme. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1076] Fluent facade API (Google summer of code 2011 project). + Contributed by Xu Lilu + +* UriBuilder implementation. + Contributed by Xu Lilu + +* Redesign of connection management classes based on new pooling components from HttpCore. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1111] Added #prepareSocket method to SSLSocketFactory. + Contributed by Pasi Eronen + +* Added #reset() and #releaseConnection() methods to HttpRequestBase. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1105] AutoRetryHttpClient: built-in way to do auto-retry for certain status codes. + Contributed by Dan Checkoway + +* [HTTPCLIENT-1094] Digest auth scheme refactoring. + Contributed by Oleg Kalnichevski + +* Lax implementation of RedirectStrategy. + Contributed by Bartosz Firyn + +* [HTTPCLIENT-1044] HttpRequestRetryHandler implementation compliant with the definition of + idempotent methods given in the RFC 2616. + Contributed by Oleg Kalnichevski + + +Release 4.1.2 +------------------- + +The HttpClient 4.1.2 is a bug fix release that addresses a number of non-critical issues reported +since release 4.1.1. + +* [HTTPCLIENT-1100] Missing Content-Length header makes cached entry invalid + Contributed by Bart Robeyns + +* [HTTPCLIENT-1098] Avoid expensive reverse DNS lookup on connect timeout exception. + Contributed by Thomas Boettcher + +* [HTTPCLIENT-1097] BrowserCompatHostnameVerifier and StrictHostnameVerifier should handle + wildcards in SSL certificates better. + Contributed by Sebastian Bazley + +* [HTTPCLIENT-1092] If ClientPNames.VIRTUAL_HOST does not provide the port, derive it from the + current request. + Contributed by Sebastian Bazley + +* [HTTPCLIENT-1087] NTLM proxy authentication fails on retry if the underlying connection is closed + as a result of a target authentication failure. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1079] Fixed Kerberos cross-realm support + Contributed by Michael Osipov <1983-01-06 at gmx.net> + +* [HTTPCLIENT-1078] Decompressing entities (DeflateDecompressingEntity, GzipDecompressingEntity) + do not close content stream in #writeTo() method. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1075] Decompressing entities (DeflateDecompressingEntity, GzipDecompressingEntity) + do not correctly handle content streaming. + Contributed by James Abley + +* [HTTPCLIENT-1051] Avoid reverse DNS lookups when opening SSL connections by IP address. + Contributed by Oleg Kalnichevski + + +Release 4.1.1 +------------------- + +HttpClient v4.1.1 is a bug fix release that addresses a number of issues reported since +release 4.1, including one critical security issue (HTTPCLIENT-1061). All users of HttpClient 4.0.x +and 4.1 are strongly encouraged to upgrade. + +* [HTTPCLIENT-1069] HttpHostConnectException not correctly retried for direct and non-tunnelled + proxy connections. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1066] Changed the way URIUtils#rewriteURI handles multiple consecutive slashes in the + URI path component: multiple leading slashes will be replaced by one slash in order to avoid + confusion with the authority component. The remaining content of the path will not be modified. + (also see HTTPCLIENT-929). + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1061] Fixed critical bug causing Proxy-Authorization header to be sent to the target + host when tunneling requests through a proxy server that requires authentication. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1056] Fixed bug causing the RequestAuthCache protocol interceptor to generate + an invalid AuthScope instance when looking up user credentials for preemptive authentication. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1053] Fixed the way DigestScheme generates nonce-count values. + Contributed by Oleg Kalnichevski + + +Release 4.1 +------------------- + +The HttpClient 4.1 release builds upon the stable foundation laid by HttpClient 4.0 and adds several +functional improvements and popular features. + +* Response caching conditionally compliant with HTTP/1.1 specification (full compliance with + MUST requirements, partial compliance with SHOULD requirements) + +* Full support for NTLMv1, NTLMv2, and NTLM2 Session authentication. The NTLM protocol code + was kindly contributed by the Lucene Connector Framework project. + +* Support for SPNEGO/Kerberos authentication. + +* Persistence of authentication data between request executions within the same execution context. + +* Support for preemptive authentication for BASIC and DIGEST schemes. + +* Support for transparent content encoding. Please note transparent content encoding is not + enabled per default in order to avoid conflicts with already existing custom content encoding + solutions. + +* Mechanism to bypass the standard certificate trust verification (useful when dealing with + self-signed certificates). + +* Simplified configuration for connection managers. + +* Transparent support for host multihoming. + +IMPORTANT: please note that the HttpClient 3.x branch is now officially END OF LIFE and is no longer +maintained and supported by the Apache HttpComponents project. + +Changelog +------------------- +* The public API for the caching module had a minor change between 4.1-beta and 4.1-GA to the + HttpCacheEntry class - the deprecated public Set getVariantURIs() method and constructor + public HttpCacheEntry(Date requestDate, Date responseDate, + StatusLine statusLine, Header[] responseHeaders, + Resource resource, Set variants) + were both removed. This will not affect you unless you are implementing new storage backends + that use the deprecated code and/or are implementing custom serializers for cache entries. + +* Changed Browser-Compatibility and Best-Match cookie policies to emulate the behaviour of FireFox + more closely when parsing Netscape style cookies. Comma will no longer be treated as a header + element separator if Set-Cookie does not contain a Version attribute mandated by the + RFC2109 / RFC 2965 cookie specifications. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1036] StringBody has incorrect default for characterset. (Default changed + to US-ASCII) + Contributed by Sebastian Bazley + +* [HTTPCLIENT-975] Support stale-if-error and stale-while-revalidate extension directive (RFC5861). + Contributed by Mohammed Azeem Uddin , + Michajlo Matijkiw , and + Matthew Hawthorne . + +* [HTTPCLIENT-1033] HttpRoute.equals(Object o) is quite inefficient, as it does not take full + advantage of shortcut logic. + Contributed by Sebastian Bazley + +* [HTTPCLIENT-1030] Implement "ignoreCookies" CookieSpec + Contributed by Sebastian Bazley + +Release 4.1 BETA1 +------------------- + +HttpClient 4.1 BETA1 finalizes the 4.1 API and brings a number of major improvements to the HTTP +caching module. This release also adds full support for NTLMv1, NTLMv2, and NTLM2 Session +authentication. The NTLM protocol code was kindly contributed by the Lucene Connector Framework +project. + +Changelog +------------------- +* [HTTPCLIENT-1015] Support only-if-cached directive. + Contributed by Michajlo Matijkiw + +* [HTTPCLIENT-990] Allow heuristic freshness caching. + Contributed by Michajlo Matijkiw + +* [HTTPCLIENT-919] Support for NTLMv1, NTLMv2, and NTLM2 Session authentication. + Contributed by Karl Wright + +* [HTTPCLIENT-1008] Send all variants' ETags on "variant miss". + Contributed by Michajlo Matijkiw and + Mohammed Azeem Uddin + +* [HTTPCLIENT-1011] Handling of IOExceptions thrown by cache components. + Contributed by Jonathan Moore + +* [HTTPCLIENT-1003] Handle conditional requests in cache. + Contributed by Michajlo Matijkiw and + Mohammed Azeem Uddin + +* [HTTPCLIENT-1002] Stale connection check fails if wire logging is on. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-1000] Maximum connection lifetimes settings for ThreadSafeClientConnManager. + Contributed by Michajlo Matijkiw + +* [HTTPCLIENT-960] HttpMultipart doesn't generate Content-Type header for binary parts in + BROWSER_COMPATIBLE mode. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-998] Cache should use both Last-Modified and ETag for validations when available. + Contributed by Jonathan Moore + +* [HTTPCLIENT-997] Cache module should handle out-of-order validations properly and unconditionally + refresh. + Contributed by Jonathan Moore + +* [HTTPCLIENT-994] Cache does not allow client to override origin-specified freshness using + max-stale. + Contributed by Jonathan Moore + +* [HTTPCLIENT-995] Cache returns cached responses even if validators not consistent with all + conditional headers. + Contributed by Jonathan Moore + +* [HTTPCLIENT-977] Memcached implementation for HttpCache. + Contributed by Mohammed Azeem Uddin + +* [HTTPCLIENT-992] cache should not generate stale responses to requests explicitly requesting + first-hand or fresh ones. + Contributed by Jonathan Moore + +* [HTTPCLIENT-991] cache module produces improperly formatted Warning header when revalidation + fails. + Contributed by Jonathan Moore + +* [HTTPCLIENT-989] DefaultHttpRequestRetryHandler no longer retries non-idempotent http methods + if NoHttpResponseException is thrown. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-988] Cache module should strip 'Content-Encoding: identity' from responses + Contributed by Jonathan Moore + +* [HTTPCLIENT-987] cache module does not recognize equivalent URIs. + Contributed by Jonathan Moore + +* [HTTPCLIENT-986] cache module does not completely handle upstream Warning headers correctly + Contributed by Jonathan Moore + +* [HTTPCLIENT-985] cache module should populate Via header to capture upstream and downstream protocols + Contributed by Jonathan Moore + +* [HTTPCLIENT-984] Additional conditional compliance tests for the caching module for + Content-Encoding, Content-Location, Date, Expires, Server, Transfer-Encoding, and Vary headers. + Contributed by Jonathan Moore + +* [HTTPCLIENT-978] HTTP cache update exception handling + Contributed by Michajlo Matijkiw + +* [HTTPCLIENT-981] CachingHttpClient returns a 411 respones when executing a POST (HttpPost) + request. + Contributed by Joe Campbell + +* [HTTPCLIENT-980] CachingHttpClient returns a 503 response when the backend HttpClient produces + an IOException. + Contributed by Jonathan Moore + +* [HTTPCLIENT-978] Ehcache based HTTP cache implementation + Contributed by Michajlo Matijkiw + +* [HTTPCLIENT-967] support for non-shared (private) caches + Contributed by Jonathan Moore + +* [HTTPCLIENT-969] BasicCookieStore#getCookies() to return a copy of Cookie list + Contributed by David Smiley + +* [HTTPCLIENT-965] Fixed problem with cache not honoring must-revalidate or + proxy-revalidate Cache-Control directives. + Contributed by Jonathan Moore + +* [HTTPCLIENT-964] 'no-cache' directives with field names are no longer transmitted + downstream. + Contributed by Jonathan Moore + +* [HTTPCLIENT-963] Fixed handling of 'Cache-Control: no-store' on requests. + Contributed by Jonathan Moore + +* [HTTPCLIENT-962] Fixed handling of Authorization headers in shared cache mode. + Contributed by Jonathan Moore + +* [HTTPCLIENT-961] Not all applicable URIs are invalidated on PUT/POST/DELETEs + that pass through client cache. + Contributed by Jonathan Moore + +* [HTTPCLIENT-958] Client cache no longer allows incomplete responses to be + passed on to the client. + Contributed by Jonathan Moore + +* [HTTPCLIENT-951] Non-repeatable entity enclosing requests are not correctly + retried when 'expect-continue' handshake is active. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-948] In rare circumstances the idle connection handling code + can leave closed connections in a inconsistent state. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-953] IllegalStateException thrown by RouteSpecificPool. + Contributed by Guillaume + +* [HTTPCLIENT-952] Trust store parameter is ignored by SSLSocketFactory + (affects version 4.1-alpha2 only) + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-937] CacheEntry made immutable; now uses immutable HttpEntity + to store cached content. + Contributed by David Mays and + Oleg Kalnichevski + +Release 4.1 ALPHA2 +------------------- + +HttpClient 4.1 ALPHA2 fixes a number of non-severe bugs discovered since +the last release and introduces support for two frequently requested features: + +* HTTP/1.1 response caching + +* transparent support for host multihoming + +* a mechanism to bypass the standard certificate trust verification +(useful when dealing with self-signed certificates) + +Compatibility notes +------------------- +(1) Please note the HTTP caching module is still considered experimental and +its API may change significantly in the future releases. + +(2) This release eliminates Mime4J as a dependency for the HttpMime module. +HttpMime is no longer binary compatible with the previous releases. +Full API and binary compatibility between minor versions of HttpMime will be +maintained as of 4.1 GA release. + +Changelog +------------------- + +* [HTTPCLIENT-936] Fixed bug causing NPE or an infinite loop in + the authentication code in case of a SPNEGO authentication failure. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-427] HTTP caching support + Contributed by Joe Campbell, David Cleaver, David Mays, Jon Moore, Brad Spenla + +* Dropped dependency on Mime4j for HttpMime. + Contributed by Oleg Kalnichevski + +* Extended SSLSocketFactory with a mechanism to bypass the standard certificate + trust verification (primarily to simplify dealing with self-signed + certificates) + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-898] Improved support for host multihoming + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-916] UsernamePasswordCredentials, NTUserPrincipal, + BasicClientCookie, BasicClientCookie2 and BasicCookieStore made Serializable. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-914] Upgraded Commons Codec dependency to version 1.4 + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-903] Use ConcurrentHashMap instead of [Linked]HashMap for + thread-safety. Improve performance of AuthSchemeRegistry, CookieSpecRegistry + and SchemeRegistry classes. + Contributed by Sebastian Bazley + +* [HTTPCLIENT-902] HttpRequestRetryHandler not called on I/O exceptions + thrown when opening a new connection. + Contributed by Olivier Lamy and + Oleg Kalnichevski + +Release 4.1 ALPHA1 +------------------- + +HttpClient 4.1 ALPHA1 builds on the stable 4.0 release and adds several +functionality improvements and new features. + +* Simplified configuration of connection managers. + +* Persistence of authentication data between request executions within + the same execution context. + +* Support for SPNEGO/Kerberos authentication scheme + +* Support for transparent content encoding. Please note transparent content + encoding is not enabled per default in order to avoid conflicts with + already existing custom content encoding solutions. + +* 5 to 10% performance increase due to elimination of unnecessary Log object + lookups by short-lived components. + +Please note all methods and classes added in this release and marked as +4.1 are API unstable and can change in the future 4.1 ALPHA releases. + +Changelog +------------------- + +* [HTTPCLIENT-889] 'expect: continue' handshake disabled per default. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-862] Extended client's redirect handling interface to allow + control of the content of the redirect. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-872] HttpClient can now persist authentication data between request + executions as long as they share the same execution context. It has also become + much easier to make HttpClient authenticate preemptively by pre-populating + authentication data cache. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-883] SO_TIMEOUT is not reset on persistent (re-used) connections. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-832] Distinguish cookie format errors from violations of + restrictions imposed by a cookie specification. In the latter case + CookieRestrictionViolationException will be thrown. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-523] Support for SPNEGO authentication scheme. + Contributed by Matthew Stevenson + +* Simplified configuration of connection managers. Total connection maximum + and maximum connection per route limits can be set using methods of + the class instead of HTTP parameters. + Contributed by Oleg Kalnichevski + +* Added parameters to define the order of preference for supported auth + schemes for target host and proxy authentication. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-875] DefaultClientConnectionOperator#openConnection doesn't + update the connection state if the connection socket changed after + the call to SocketFactory#connectSocket(). + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-834] Transparent content encoding support. + Contributed by James Abley + +Release 4.0.1 +------------------- + +This is a bug fix release that addresses a number of issues discovered since +the previous stable release. None of the fixed bugs is considered critical. +Most notably this release eliminates eliminates dependency on JCIP annotations. + +This release is also expected to improve performance by 5 to 10% due to +elimination of unnecessary Log object lookups by short-lived components. + +Changelog +------------------- + +* [HTTPCLIENT-895] Eliminated Log lookups in short lived objects impairing + performance. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-885] URLEncodedUtils now correctly parses form-url-encoded + entities that specify a charset. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-884] UrlEncodedFormEntity now sets charset on the Content-Type + header. + Contributed by Jared Jacobs + +* [HTTPCLIENT-883] SO_TIMEOUT is not reset on persistent (re-used) connections. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-882] Auth state is now correctly updated if a successful NTLM + authentication results in a redirect. This is a minor bug as HttpClient + manages to recover from the problem automatically. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-881] Fixed race condition in AbstractClientConnAdapter that makes + it possible for an aborted connection to be returned to the pool. + Contributed by Tim Boemker and + Oleg Kalnichevski + +* [HTTPCLIENT-866] Removed dependency on jcip-annotations.jar. + Contributed by Oleg Kalnichevski + and Sebastian Bazley + + +Release 4.0 +------------------- + +HttpClient 4.0 represents a complete, ground-up redesign and almost a complete +rewrite of the HttpClient 3.x codeline. This release finally addresses several +design flaws that existed since the 1.0 release and could not be fixed without +a major code overhaul and breaking API compatibility. + + +Architectural changes +--------------------- + +* Redesign of the HttpClient internals addressing all known major + architectural shortcomings of the 3.x codeline. + +* Cleaner, more flexible and expressive API. + +* More modular structure. + +* Better performance and smaller memory footprint due to a more efficient HTTP + transport based on HttpCore. + +* Implementation of cross-cutting HTTP protocol aspects through protocol + interceptors. + +* Improved connection management, better handling of persistent connections, + support for stateful connections + +* Pluggable redirect and authentication handlers. + +* Improved support for sending requests via a proxy or a chain of proxies + +* More flexible SSL context customization + +* Reduced intermediate garbage in the process of generating HTTP requests + and parsing HTTP responses + + +Important notes +------------------- + +* Future releases of HttpMime module may be binary incompatible with this + release due to possible API changes in Apache Mime4J. Apache Mime4J is + still being actively developed and its API is considered unstable. + +* HttpClient 4.0 is not fully binary compatible with 4.0 BETA1 release. + Some protected variables in connection management class have been + made final in order to help ensure their thread safety: + + org.apache.http.conn.BasicEofSensorWatcher#attemptReuse + org.apache.http.conn.BasicEofSensorWatcher#managedConn + org.apache.http.impl.conn.DefaultClientConnectionOperator#schemeRegistry + org.apache.http.impl.conn.DefaultHttpRoutePlanner#schemeRegistry + org.apache.http.impl.conn.ProxySelectorRoutePlanner#schemeRegistry + org.apache.http.impl.conn.SingleClientConnManager#alwaysShutDown + org.apache.http.impl.conn.SingleClientConnManager#connOperator + org.apache.http.impl.conn.SingleClientConnManager#schemeRegistry + org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager#connOperator + org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager#schemeRegistry + + +Bug fixes since 4.0 BETA2 release +------------------- + +* [HTTPCLIENT-861] URIUtils#resolve is now compatible with all examples given + in RFC 3986. + Contributed by Johannes Koch + +* [HTTPCLIENT-860] HttpClient no longer converts redirects of PUT/POST to GET + for status codes 301, 302, 307, as required by the HTTP spec. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-859] CookieIdentityComparator now takes path attribute into + consideration when comparing cookies. + Contributed by Oleg Kalnichevski + +* HttpClient will no longer send expired cookies back to the origin server. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-856] Proxy NTLM authentication no longer fails on a redirect to + a different host. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-841] Removed automatic connection release using garbage collection + due to a memory leak. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-853] Fixed bug causing invalid cookie origin port to be selected + when the target is accessed on the default port and the connection is + established via a proxy. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-852] Fixed bug causing automatically retried redirects fail with + CircularRedirectException. + Contributed by Oleg Kalnichevski + +* Fixed problem with the default HTTP response parser failing to handle garbage + preceding a valid HTTP response. + Contributed by Oleg Kalnichevski + +* NonRepeatableRequestExceptions now include the cause that the original + request failed. + Contributed by Sam Berlin + +* [HTTPCLIENT-837] Fixed problem with the wire log skipping zero byte values + if read one byte at a time. + Contributed by Kirill Safonov + +* [HTTPCLIENT-823] 'http.conn-manager.max-total' parameter can be adjusted + dynamically. However, the size of existing connection pools per route, + once allocated, will not be adjusted. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-822] Default socket factories to rethrow SocketTimeoutException + as ConnectTimeoutException in case of connect failure due to a time out. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-813] Fixed default port resolution. Invalid ports no longer + get replaced with the default port value. + Contributed by Oleg Kalnichevski + +Release 4.0 beta 2 +------------------- + +BETA2 is a maintenance release, which addresses a number of issues +discovered since the previous release. + +The only significant new feature is an addition of an OSGi compliant +bundle combining HttpClient and HttpMime jars. + +All upstream projects are strongly encouraged to upgrade. + +* Fixed NPE in DefaultRequestDirector thrown when retrying a failed + request over a proxied connection. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-803] Fixed bug in SSL host verifier implementations + causing the SSL certificate to be rejected as invalid if the connection + is established using an IP address. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-806] DefaultHttpMethodRetryHandler will no longer retry + on ConnectExceptions. + Contributed by Oleg Kalnichevski + +* DigestScheme can use an arbitrary digest algorithm requested by the + target server (such as SHA) as long as this algorithm is supported by + the Java runtime. + Contributed by Oleg Kalnichevski + +* Fixed parsing and validation of RFC2109 compliant Set-Cookie headers + by the Best-Match cookie spec. + Contributed by Oleg Kalnichevski + +* Fixed bug that can cause a managed connection to be returned from the + pool in an inconsistent state. + Contributed by Oleg Kalnichevski + + +4.0 Beta 1 +------------------- + +BETA1 release brings yet another round of API enhancements and +improvements in the area of connection management. Among the most notable +ones is the capability to handle stateful connections such as persistent +NTLM connections and private key authenticated SSL connections. + +This is the first API stable release of HttpClient 4.0. All further +releases in the 4.0 code line will maintain API compatibility with this +release. + +There has been a number of important bug fixes since ALPHA4. All upstream +projects are encouraged to upgrade to the latest release. + +Please note HttpClient currently provides only limited support for NTLM +authentication. For details please see NTLM_SUPPORT.txt. + +------------------- + +Changelog: +------------------- + +* [HTTPCLIENT-790] Protocol interceptors are now correctly invoked when + executing CONNECT methods. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-668] Do not use static loggers. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-781] Respect Keep-Alive header's timeout value. + Contributed by Sam Berlin + +* [HTTPCLIENT-779] Top-level classes (HttpClient, and HttpGet, HttpPut + and similar HttpMethods) throw fewer checked exceptions. + Contributed by Sam Berlin + +* HttpClient will throw an exception if an attempt is made to retry + a request with a non-repeatable request entity. + Contributed by Oleg Kalnichevski + +* Fixed request re-generation logic when retrying a failed request. + Auto-generated headers will no accumulate. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-424] Preemptive authentication no longer limited to BASIC + scheme only. HttpClient can be customized to authenticate preemptively + with DIGEST scheme. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-670] Pluggable hostname resolver. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-719] Clone support for HTTP request and cookie objects. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-776] Fixed concurrency issues with AbstractPoolEntry. + Contributed by Sam Berlin + +* Resolved a long standing problem with HttpClient not taking into account + the user context when pooling / re-using connections. HttpClient now + correctly handles stateful / user specific connections such as persistent + NTLM connections and SSL connections with client side authentication. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-773] Improved handling of the 'expires' attribute by the + 'Best Match' cookie spec. + Contributed by Oleg Kalnichevski + +* Partial NTLM support (requires an external NTLM engine). For details see + NTLM_SUPPORT.txt + Contributed by Oleg Kalnichevski + +* Redesigned local execution context management. + Contributed by Oleg Kalnichevski + +-------------------------------------- + +Release 4.0 Alpha 4 +------------------- + +ALPHA4 marks the completion of the overhaul of the connection management +code in HttpClient. All known shortcomings of the old HttpClient 3.x +connection management API have been addressed. + +NTLM authentication remains the only missing major feature in the new +codeline that prevents us from moving awards the API freeze. + +There has been a number of important bug fixes since ALPHA3. All upstream +projects are encouraged to upgrade to the latest release. + +------------------- + +HttpClient 3.x features that have NOT yet been ported: +------------------- + +* NTLM authentication scheme + +------------------- + +Changelog: +------------------- + +* [HTTPCLIENT-765] String.toLowerCase() / toUpperCase() should specify + Locale.ENGLISH + Contributed by Sebastian Bazley + +* [HTTPCLIENT-769] Do not pool connection marked non-reusable. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-763] Fixed problem with AbstractClientConnAdapter#abortConnection() + not releasing the connection if called from the main execution thread while + there is no blocking I/O operation. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-652] Added optional state attribute to managed client connections. + This enables connection managers to correctly handle stateful connections. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-673] Revised max connections per route configuration + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-753] Class Scheme and related classes moved to a separate package + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-757] Improved request wrapping in the DefaultClientRequestDirector. + This also fixed the problem with the default proxy set at the client level + having no effect. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-734] Request abort will unblock the thread waiting for a connection + Contributed by Sam Berlin + +* [HTTPCLIENT-759] Ensure release of connections back to the connection manager + on exceptions. + Contributed by Sam Berlin + +* [HTTPCLIENT-758] Fixed the use of generics in AbstractHttpClient + #removeRequestInterceptorByClass and #removeResponseInterceptorByClass + Contributed by Johannes Koch + +* [HTTPCLIENT-749] HttpParams beans + Contributed by Stojce Dimski + +* [HTTPCLIENT-755] Workaround for known bugs in java.net.URI.resolve() + Bug ID: 4708535 + Contributed by Johannes Koch + +-------------------------------------- + +Release 4.0 Alpha 3 +------------------- + +ALPHA3 release brings another round of API refinements and improvements in +functionality. As of this release HttpClient requires Java 5 compatible +runtime environment and takes full advantage of generics and new concurrency +primitives. + +This release also introduces new default cookie policy that selects a cookie +specification depending on the format of cookies sent by the target host. +It is no longer necessary to know beforehand what kind of HTTP cookie support +the target host provides. HttpClient is now able to pick up either a lenient +or a strict cookie policy depending on the compliance level of the target host. + +Another notable improvement is a completely reworked support for multipart +entities based on Apache mime4j library. + +------------------- + +HttpClient 3.x features that have NOT yet been ported: +------------------- + +* NTLM authentication scheme + +------------------- + +Changelog: +------------------- + +* [HTTPCLIENT-742] common interface for HttpRoute and RouteTracker + Contributed by Roland Weber + +* [HTTPCLIENT-741] Fixed concurrency issues in AbstractClientConnAdapter. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-726] testcase for spurious wakeups in ThreadSafeClientConnManager + Contributed by Roland Weber + +* [HTTPCLIENT-643] Automatic connect fail-over for multi-home remote servers. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-735] unsetting of DEFAULT_PROXY and FORCED_ROUTE in hierarchies + Contributed by Roland Weber + +* [HTTPCLIENT-723] route planner based on java.net.ProxySelector + Contributed by Roland Weber + +* [HTTPCLIENT-740] don't start connection GC thread in pool constructor + Contributed by Roland Weber + +* [HTTPCLIENT-736] route planners use SchemeRegistry instead of ConnManager + Contributed by Roland Weber + +* [HTTPCLIENT-730] Fixed rewriting of URIs containing escaped characters + Contributed by Sam Berlin and + Oleg Kalnichevski + +* [HTTPCLIENT-667] Added 'Meta' cookie policy that selects a cookie + specification depending on the format of the cookie(s). + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-729] Move HttpRoute and related classes to routing package. + Contributed by Roland Weber + +* [HTTPCLIENT-725] Use TimeUnit arguments for timeouts in connection manager. + Contributed by Roland Weber + +* [HTTPCLIENT-677] Connection manager no longer uses Thread.interrupt(). + Contributed by Roland Weber + +* [HTTPCLIENT-716] Allow application-defined routes. + Contributed by Roland Weber + +* [HTTPCLIENT-712] Improve HttpRoute API + Contributed by Roland Weber + +* [HTTPCLIENT-711] Bad route computed for redirected requests + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-715] Remove RoutedRequest from API + Contributed by Roland Weber + +* [HTTPCLIENT-705] Fixed incorrect handling of URIs with null path component. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-688] HttpOptions#getAllowedMethods can now handle multiple + Allow headers. + Contributed by Andrea Selva + +-------------------------------------- + +Release 4.0 Alpha 2 +------------------- + +ALPHA2 release is another milestone in the redesign of HttpClient. It includes +a number of improvements since ALPHA1, among which are improved connection +pooling, support for proxy chains, redesigned HTTP state and authentication +credentials management API, improved RFC 2965 cookie specification. + +------------------- + +HttpClient 3.x features that have NOT yet been ported +------------------- +* NTLM authentication scheme + +* Support for multipart MIME coded entities + +------------------- + +Changelog +------------------- + +* [HTTPCLIENT-698] Resolve non-absolute redirect URIs relative to + the request URI + Contributed by Johannes Koch + +* [HTTPCLIENT-697] Throw a more intelligible exception when connection + to a remote host cannot be established. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-689] Caching of SimpleDateFormat in DateUtils + Contributed by Daniel Müller + +* [HTTPCLIENT-689] stackable parameters in AbstractHttpClient + Contributed by Roland Weber + +* [HTTPCLIENT-477] Use distinct instances of the authentication handler + interface for authentication with target and proxy hosts + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-690] ManagedClientConnection provides access to SSLSession + Contributed by Roland Weber + +* [HTTPCLIENT-692] ClientConnectionManager throws InterruptedException + Contributed by Roland Weber + +* [HTTPCORE-116] moved parameter names to interfaces + Contributed by Roland Weber + +* [HTTPCLIENT-649] support for proxy chains in HttpConn + Contributed by Roland Weber + +* [HTTPCLIENT-636] refactor ThreadSafeClientConnManager in separate package + Contributed by Roland Weber + +* [HTTPCLIENT-669] new HttpRoutePlanner interface and implementation + Contributed by Andrea Selva + +* [HTTPCLIENT-653] detached connection wrapper no longer prevents + garbage collection of ThreadSafeClientConnManager + Contributed by Roland Weber + +* [HTTPCLIENT-674] use org.apache.http.util.VersionInfo instead of a local one + Contributed by Roland Weber + +* [HTTPCLIENT-666] Replaced HttpState with CredentialsProvier and CookieStore interfaces + Contributed by Oleg Kalnichevski + +* [HTTPCORE-100] revised HttpContext hierarchy + Contributed by Roland Weber + +* [HTTPCLIENT-618] eliminate class HostConfiguration + Contributed by Roland Weber + +* [HTTPCLIENT-672] re-sync with API changes in core alpha6-SNAPSHOT + Contributed by Roland Weber + +-------------------------------------- + +Release 4.0 Alpha 1 +------------------- + +HttpClient 4.0 represents a complete, ground-up redesign and almost a complete +rewrite of the HttpClient 3.x codeline. This release finally addresses several +design flaws that existed since the 1.0 release and could not be fixed without +a major code overhaul and breaking API compatibility. + +The HttpClient 4.0 API is still very experimental and is bound to change +during the course of the ALPHA development phase. Several important features +have not yet been ported to the new API. + +Architectural changes +--------------------- + +* Redesign of the HttpClient internals addressing all known + major architectural shortcomings of the 3.x codeline + +* Cleaner, more flexible and expressive API + +* Better performance and smaller memory footprint due to a more + efficient HTTP transport based on HttpCore. HttpClient 4.0 is + expected to be 10% to 25% faster than HttpClient 3.x codeline + +* More modular structure + +* Pluggable redirect and authentication handlers + +* Support for protocol incerceptors + +* Improved connection management + +* Improved support for sending requests via a proxy or a chain of + proxies + +* Improved handling redirects of entity enclosing requests + +* More flexible SSL context customization + +* Reduced intermediate garbage in the process of + generating HTTP requests and parsing HTTP responses + +------------------- + +HttpClient 3.x features that have NOT yet been ported +------------------- +* NTLM authentication scheme + +* RFC2965 cookie policy (Cookie2) + +* Support for multipart MIME coded entities + +------------------- + +Changelog +------------------- + +The following is a list of contributions tracked in JIRA. +Note that this is not a complete list of contributions or changes. +Since the API was redesigned completely, tracking everything outside +of the source code repository would have been too burdensome. + +* [HTTPCLIENT-655] User-Agent string no longer violates RFC + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-541] Virtual host API redesign + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-614] Allow for different strategies when checking + CN of x509 certificates + Contributed by Julius Davies + +* [HTTPCLIENT-136] Fixed inadequate proxy support + Long standing architectural problem. Issue opened on 19/Dec/2002. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-63] Support for pluggable redirect and authentication handlers + Long standing architectural problem. Issue opened on 15/Jul/2002. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-245] Fixed redirect handling. HttpClient can now automatically + handle redirects of entity enclosing requests. + Long standing architectural problem. Issue opened on 14/Jul/2003. + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-613] HTTPS connections now verify CN of x509 certificates + Contributed by Julius Davies + +* [HTTPCLIENT-497] Wire/header logger names consistent with class loggers + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-484] AuthSSLProtocolSocketFactory in the main distribution + Contributed by Oleg Kalnichevski + +* [HTTPCLIENT-589] Do not consume the remaining response content if + the connection is to be closed + Contributed by Roland Weber + +* [HTTPCLIENT-475] Support for unconnected sockets. HTTP requests can now be + aborted while network socket is still being connected. + Contributed by Roland Weber + +ub From 3fb2e4a0f133eca10f5858bb4e94977e960c55b4 Mon Sep 17 00:00:00 2001 From: xfeep Date: Thu, 10 Aug 2017 23:30:56 +0800 Subject: [PATCH 3/6] upgrade asm4 to asm5 --- .../nginx/clojure/asm/AnnotationVisitor.java | 10 +- .../nginx/clojure/asm/AnnotationWriter.java | 55 +- src/java/nginx/clojure/asm/ByteVector.java | 97 +- src/java/nginx/clojure/asm/ClassReader.java | 468 +++++- src/java/nginx/clojure/asm/ClassVisitor.java | 46 +- src/java/nginx/clojure/asm/ClassWriter.java | 212 ++- src/java/nginx/clojure/asm/Context.java | 35 + src/java/nginx/clojure/asm/CurrentFrame.java | 56 + src/java/nginx/clojure/asm/FieldVisitor.java | 41 +- src/java/nginx/clojure/asm/FieldWriter.java | 58 +- src/java/nginx/clojure/asm/Frame.java | 137 +- src/java/nginx/clojure/asm/Handle.java | 62 +- src/java/nginx/clojure/asm/Item.java | 4 +- src/java/nginx/clojure/asm/Label.java | 18 +- src/java/nginx/clojure/asm/MethodVisitor.java | 255 ++- src/java/nginx/clojure/asm/MethodWriter.java | 954 ++++------- src/java/nginx/clojure/asm/Opcodes.java | 24 +- src/java/nginx/clojure/asm/Type.java | 42 +- src/java/nginx/clojure/asm/TypePath.java | 196 +++ src/java/nginx/clojure/asm/TypeReference.java | 452 +++++ .../clojure/asm/commons/AdviceAdapter.java | 29 +- .../clojure/asm/commons/AnalyzerAdapter.java | 37 +- .../asm/commons/AnnotationRemapper.java | 79 + .../clojure/asm/commons/ClassRemapper.java | 132 ++ .../asm/commons/CodeSizeEvaluator.java | 25 +- .../clojure/asm/commons/FieldRemapper.java | 71 + .../clojure/asm/commons/GeneratorAdapter.java | 35 +- .../asm/commons/InstructionAdapter.java | 104 +- .../asm/commons/JSRInlinerAdapter.java | 9 +- .../asm/commons/LocalVariablesSorter.java | 40 +- .../nginx/clojure/asm/commons/Method.java | 4 +- .../clojure/asm/commons/MethodRemapper.java | 223 +++ .../nginx/clojure/asm/commons/Remapper.java | 35 +- .../commons/RemappingAnnotationAdapter.java | 4 +- .../asm/commons/RemappingClassAdapter.java | 17 +- .../asm/commons/RemappingFieldAdapter.java | 13 +- .../asm/commons/RemappingMethodAdapter.java | 83 +- .../commons/RemappingSignatureAdapter.java | 4 +- .../asm/commons/SerialVersionUIDAdder.java | 25 +- .../asm/commons/SignatureRemapper.java | 159 ++ .../clojure/asm/commons/SimpleRemapper.java | 8 +- .../clojure/asm/commons/StaticInitMerger.java | 5 +- .../asm/commons/TryCatchBlockSorter.java | 6 +- .../nginx/clojure/asm/commons/package.html | 48 + .../AnnotationConstantsCollector.java | 147 ++ .../optimizer/ClassConstantsCollector.java | 198 +++ .../clojure/asm/optimizer/ClassOptimizer.java | 259 +++ .../nginx/clojure/asm/optimizer/Constant.java | 336 ++++ .../clojure/asm/optimizer/ConstantPool.java | 251 +++ .../optimizer/FieldConstantsCollector.java | 89 + .../clojure/asm/optimizer/JarOptimizer.java | 235 +++ .../optimizer/MethodConstantsCollector.java | 224 +++ .../asm/optimizer/MethodOptimizer.java | 180 ++ .../clojure/asm/optimizer/NameMapping.java | 114 ++ .../nginx/clojure/asm/optimizer/Shrinker.java | 282 ++++ .../clojure/asm/optimizer/jdk1.2.2_017.txt.gz | Bin 0 -> 113814 bytes .../clojure/asm/optimizer/jdk1.3.1_19.txt.gz | Bin 0 -> 128067 bytes .../optimizer/shrink-annotations.properties | 53 + .../asm/optimizer/shrink-frames.properties | 62 + .../asm/optimizer/shrink-resize.properties | 37 + .../optimizer/shrink-signatures.properties | 43 + .../asm/optimizer/shrink-writer.properties | 66 + .../clojure/asm/optimizer/shrink.properties | 379 +++++ src/java/nginx/clojure/asm/package.html | 87 + .../asm/signature/SignatureReader.java | 8 +- .../asm/signature/SignatureVisitor.java | 9 +- .../asm/signature/SignatureWriter.java | 6 +- .../nginx/clojure/asm/signature/package.html | 36 + .../clojure/asm/tree/AbstractInsnNode.java | 78 + .../clojure/asm/tree/AnnotationNode.java | 90 +- .../nginx/clojure/asm/tree/ClassNode.java | 80 +- .../nginx/clojure/asm/tree/FieldInsnNode.java | 8 +- .../nginx/clojure/asm/tree/FieldNode.java | 76 +- .../nginx/clojure/asm/tree/IincInsnNode.java | 3 +- src/java/nginx/clojure/asm/tree/InsnList.java | 44 +- src/java/nginx/clojure/asm/tree/InsnNode.java | 3 +- .../nginx/clojure/asm/tree/IntInsnNode.java | 3 +- .../asm/tree/InvokeDynamicInsnNode.java | 4 +- .../nginx/clojure/asm/tree/JumpInsnNode.java | 4 +- .../nginx/clojure/asm/tree/LdcInsnNode.java | 3 +- .../asm/tree/LocalVariableAnnotationNode.java | 157 ++ .../asm/tree/LookupSwitchInsnNode.java | 5 +- .../clojure/asm/tree/MethodInsnNode.java | 36 +- .../nginx/clojure/asm/tree/MethodNode.java | 261 ++- .../asm/tree/MultiANewArrayInsnNode.java | 3 +- .../nginx/clojure/asm/tree/ParameterNode.java | 76 + .../clojure/asm/tree/TableSwitchInsnNode.java | 3 +- .../clojure/asm/tree/TryCatchBlockNode.java | 59 + .../clojure/asm/tree/TypeAnnotationNode.java | 100 ++ .../nginx/clojure/asm/tree/TypeInsnNode.java | 3 +- .../nginx/clojure/asm/tree/VarInsnNode.java | 3 +- .../clojure/asm/tree/analysis/Analyzer.java | 1 + .../asm/tree/analysis/AnalyzerException.java | 1 + .../asm/tree/analysis/BasicInterpreter.java | 2 +- .../asm/tree/analysis/BasicVerifier.java | 2 +- .../clojure/asm/tree/analysis/Frame.java | 20 +- .../asm/tree/analysis/SimpleVerifier.java | 2 +- .../asm/tree/analysis/SourceInterpreter.java | 2 +- .../clojure/asm/tree/analysis/package.html | 67 + src/java/nginx/clojure/asm/tree/package.html | 192 +++ src/java/nginx/clojure/asm/util/ASMifier.java | 154 +- .../asm/util/CheckAnnotationAdapter.java | 4 +- .../clojure/asm/util/CheckClassAdapter.java | 119 +- .../clojure/asm/util/CheckFieldAdapter.java | 26 +- .../clojure/asm/util/CheckMethodAdapter.java | 170 +- .../asm/util/CheckSignatureAdapter.java | 4 +- src/java/nginx/clojure/asm/util/Printer.java | 851 +++++++++- .../nginx/clojure/asm/util/Textifier.java | 303 +++- .../asm/util/TraceAnnotationVisitor.java | 2 +- .../clojure/asm/util/TraceClassVisitor.java | 13 +- .../clojure/asm/util/TraceFieldVisitor.java | 13 +- .../clojure/asm/util/TraceMethodVisitor.java | 77 +- .../asm/util/TraceSignatureVisitor.java | 18 +- src/java/nginx/clojure/asm/util/package.html | 40 + .../clojure/asm/xml/ASMContentHandler.java | 1464 +++++++++++++++++ src/java/nginx/clojure/asm/xml/Processor.java | 1045 ++++++++++++ .../nginx/clojure/asm/xml/SAXAdapter.java | 89 + .../clojure/asm/xml/SAXAnnotationAdapter.java | 243 +++ .../clojure/asm/xml/SAXClassAdapter.java | 336 ++++ .../nginx/clojure/asm/xml/SAXCodeAdapter.java | 416 +++++ .../clojure/asm/xml/SAXFieldAdapter.java | 72 + src/java/nginx/clojure/asm/xml/asm-xml.dtd | 349 ++++ src/java/nginx/clojure/asm/xml/package.html | 96 ++ 123 files changed, 13656 insertions(+), 1157 deletions(-) create mode 100644 src/java/nginx/clojure/asm/CurrentFrame.java create mode 100644 src/java/nginx/clojure/asm/TypePath.java create mode 100644 src/java/nginx/clojure/asm/TypeReference.java create mode 100644 src/java/nginx/clojure/asm/commons/AnnotationRemapper.java create mode 100644 src/java/nginx/clojure/asm/commons/ClassRemapper.java create mode 100644 src/java/nginx/clojure/asm/commons/FieldRemapper.java create mode 100644 src/java/nginx/clojure/asm/commons/MethodRemapper.java create mode 100644 src/java/nginx/clojure/asm/commons/SignatureRemapper.java create mode 100644 src/java/nginx/clojure/asm/commons/package.html create mode 100644 src/java/nginx/clojure/asm/optimizer/AnnotationConstantsCollector.java create mode 100644 src/java/nginx/clojure/asm/optimizer/ClassConstantsCollector.java create mode 100644 src/java/nginx/clojure/asm/optimizer/ClassOptimizer.java create mode 100644 src/java/nginx/clojure/asm/optimizer/Constant.java create mode 100644 src/java/nginx/clojure/asm/optimizer/ConstantPool.java create mode 100644 src/java/nginx/clojure/asm/optimizer/FieldConstantsCollector.java create mode 100644 src/java/nginx/clojure/asm/optimizer/JarOptimizer.java create mode 100644 src/java/nginx/clojure/asm/optimizer/MethodConstantsCollector.java create mode 100644 src/java/nginx/clojure/asm/optimizer/MethodOptimizer.java create mode 100644 src/java/nginx/clojure/asm/optimizer/NameMapping.java create mode 100644 src/java/nginx/clojure/asm/optimizer/Shrinker.java create mode 100644 src/java/nginx/clojure/asm/optimizer/jdk1.2.2_017.txt.gz create mode 100644 src/java/nginx/clojure/asm/optimizer/jdk1.3.1_19.txt.gz create mode 100644 src/java/nginx/clojure/asm/optimizer/shrink-annotations.properties create mode 100644 src/java/nginx/clojure/asm/optimizer/shrink-frames.properties create mode 100644 src/java/nginx/clojure/asm/optimizer/shrink-resize.properties create mode 100644 src/java/nginx/clojure/asm/optimizer/shrink-signatures.properties create mode 100644 src/java/nginx/clojure/asm/optimizer/shrink-writer.properties create mode 100644 src/java/nginx/clojure/asm/optimizer/shrink.properties create mode 100644 src/java/nginx/clojure/asm/package.html create mode 100644 src/java/nginx/clojure/asm/signature/package.html create mode 100644 src/java/nginx/clojure/asm/tree/LocalVariableAnnotationNode.java create mode 100644 src/java/nginx/clojure/asm/tree/ParameterNode.java create mode 100644 src/java/nginx/clojure/asm/tree/TypeAnnotationNode.java create mode 100644 src/java/nginx/clojure/asm/tree/analysis/package.html create mode 100644 src/java/nginx/clojure/asm/tree/package.html create mode 100644 src/java/nginx/clojure/asm/util/package.html create mode 100644 src/java/nginx/clojure/asm/xml/ASMContentHandler.java create mode 100644 src/java/nginx/clojure/asm/xml/Processor.java create mode 100644 src/java/nginx/clojure/asm/xml/SAXAdapter.java create mode 100644 src/java/nginx/clojure/asm/xml/SAXAnnotationAdapter.java create mode 100644 src/java/nginx/clojure/asm/xml/SAXClassAdapter.java create mode 100644 src/java/nginx/clojure/asm/xml/SAXCodeAdapter.java create mode 100644 src/java/nginx/clojure/asm/xml/SAXFieldAdapter.java create mode 100644 src/java/nginx/clojure/asm/xml/asm-xml.dtd create mode 100644 src/java/nginx/clojure/asm/xml/package.html 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 | + * visitParameterAnnotation visitTypeAnnotation | * 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).
  • - *
  • {@link Opcodes#F_FULL} representing complete frame data.
  • + *
  • {@link Opcodes#F_FULL} representing complete frame data.
  • + * + * * - *
    + *
    * In both cases the first frame, corresponding to the method's parameters * and access flags, is implicit and must not be visited. Also, it is * illegal to visit two or more frames for the same code location (i.e., at @@ -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 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 unused; + + public NameMapping(final String file) throws IOException { + mapping = new Properties(); + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(file)); + mapping.load(is); + unused = new HashSet(mapping.keySet()); + } finally { + if (is != null) { + is.close(); + } + } + } + + public String map(final String name) { + String s = (String) mapping.get(name); + if (s == null) { + int p = name.indexOf('.'); + if (p == -1) { + s = name; + } else { + int q = name.indexOf('('); + if (q == -1) { + s = name.substring(p + 1); + } else { + s = name.substring(p + 1, q); + } + } + } else { + unused.remove(name); + } + return s; + } + + public String fix(final String desc) { + if (desc.startsWith("(")) { + Type[] arguments = Type.getArgumentTypes(desc); + Type result = Type.getReturnType(desc); + for (int i = 0; i < arguments.length; ++i) { + arguments[i] = fix(arguments[i]); + } + result = fix(result); + return Type.getMethodDescriptor(result, arguments); + } else { + return fix(Type.getType(desc)).getDescriptor(); + } + } + + private Type fix(final Type t) { + if (t.getSort() == Type.OBJECT) { + return Type.getObjectType(map(t.getInternalName())); + } else if (t.getSort() == Type.ARRAY) { + String s = fix(t.getElementType()).getDescriptor(); + for (int i = 0; i < t.getDimensions(); ++i) { + s = '[' + s; + } + return Type.getType(s); + } else { + return t; + } + } +} diff --git a/src/java/nginx/clojure/asm/optimizer/Shrinker.java b/src/java/nginx/clojure/asm/optimizer/Shrinker.java new file mode 100644 index 00000000..a9c0a58b --- /dev/null +++ b/src/java/nginx/clojure/asm/optimizer/Shrinker.java @@ -0,0 +1,282 @@ +/*** + * 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.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; + +import nginx.clojure.asm.ClassReader; +import nginx.clojure.asm.ClassWriter; +import nginx.clojure.asm.Handle; +import nginx.clojure.asm.Type; +import nginx.clojure.asm.commons.Remapper; +import nginx.clojure.asm.commons.SimpleRemapper; + +/** + * A class file shrinker utility. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class Shrinker { + + static final HashMap MAPPING = new HashMap(); + + public static void main(final String[] args) throws IOException { + Properties properties = new Properties(); + int n = args.length - 1; + for (int i = 0; i < n - 1; ++i) { + properties.load(new FileInputStream(args[i])); + } + + for (Map.Entry entry : properties.entrySet()) { + MAPPING.put((String) entry.getKey(), (String) entry.getValue()); + } + + final Set unused = new HashSet(MAPPING.keySet()); + + File f = new File(args[n - 1]); + File d = new File(args[n]); + + optimize(f, d, new SimpleRemapper(MAPPING) { + @Override + public String map(String key) { + String s = super.map(key); + if (s != null) { + unused.remove(key); + } + return s; + } + }); + + Iterator i = unused.iterator(); + while (i.hasNext()) { + String s = i.next(); + if (!s.endsWith("/remove")) { + System.out.println("INFO: unused mapping " + s); + } + } + } + + static void optimize(final File f, final File d, final Remapper remapper) + throws IOException { + if (f.isDirectory()) { + File[] files = f.listFiles(); + for (int i = 0; i < files.length; ++i) { + optimize(files[i], d, remapper); + } + } else if (f.getName().endsWith(".class")) { + ConstantPool cp = new ConstantPool(); + ClassReader cr = new ClassReader(new FileInputStream(f)); + // auto-boxing removal requires to recompute the maxs + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + ClassConstantsCollector ccc = new ClassConstantsCollector(cw, cp); + ClassOptimizer co = new ClassOptimizer(ccc, remapper); + cr.accept(co, ClassReader.SKIP_DEBUG); + + Set constants = new TreeSet( + new ConstantComparator()); + constants.addAll(cp.values()); + + cr = new ClassReader(cw.toByteArray()); + cw = new ClassWriter(0); + Iterator i = constants.iterator(); + while (i.hasNext()) { + Constant c = i.next(); + c.write(cw); + } + cr.accept(cw, ClassReader.SKIP_DEBUG); + + if (MAPPING.get(cr.getClassName() + "/remove") != null) { + return; + } + String n = remapper.mapType(cr.getClassName()); + File g = new File(d, n + ".class"); + if (!g.exists() || g.lastModified() < f.lastModified()) { + if (!g.getParentFile().exists() && !g.getParentFile().mkdirs()) { + throw new IOException("Cannot create directory " + + g.getParentFile()); + } + OutputStream os = new FileOutputStream(g); + try { + os.write(cw.toByteArray()); + } finally { + os.close(); + } + } + } + } + + static class ConstantComparator implements Comparator { + + public int compare(final Constant c1, final Constant c2) { + int d = getSort(c1) - getSort(c2); + if (d == 0) { + switch (c1.type) { + case 'I': + return ((Integer)c1.intVal).compareTo(c2.intVal); + case 'J': + return ((Long)c1.longVal).compareTo(c2.longVal); + case 'F': + return ((Float)c1.floatVal).compareTo(c2.floatVal); + case 'D': + return ((Double)c1.doubleVal).compareTo(c2.doubleVal); + case 's': + case 'S': + case 'C': + case 't': + return c1.strVal1.compareTo(c2.strVal1); + case 'T': + d = c1.strVal1.compareTo(c2.strVal1); + if (d == 0) { + d = c1.strVal2.compareTo(c2.strVal2); + } + break; + case 'y': + d = c1.strVal1.compareTo(c2.strVal1); + if (d == 0) { + d = c1.strVal2.compareTo(c2.strVal2); + if (d == 0) { + Handle bsm1 = (Handle) c1.objVal3; + Handle bsm2 = (Handle) c2.objVal3; + d = compareHandle(bsm1, bsm2); + if (d == 0) { + d = compareObjects(c1.objVals, c2.objVals); + } + } + } + break; + + default: + d = c1.strVal1.compareTo(c2.strVal1); + if (d == 0) { + d = c1.strVal2.compareTo(c2.strVal2); + if (d == 0) { + d = ((String) c1.objVal3) + .compareTo((String) c2.objVal3); + } + } + } + } + return d; + } + + private static int compareHandle(Handle h1, Handle h2) { + int d = h1.getTag() - h2.getTag(); + if (d == 0) { + d = h1.getOwner().compareTo(h2.getOwner()); + if (d == 0) { + d = h1.getName().compareTo(h2.getName()); + if (d == 0) { + d = h1.getDesc().compareTo(h2.getDesc()); + } + } + } + return d; + } + + private static int compareType(Type mtype1, Type mtype2) { + return mtype1.getDescriptor().compareTo(mtype2.getDescriptor()); + } + + @SuppressWarnings("unchecked") + private static int compareObjects(Object[] objVals1, Object[] objVals2) { + int length = objVals1.length; + int d = length - objVals2.length; + if (d == 0) { + for (int i = 0; i < length; i++) { + Object objVal1 = objVals1[i]; + Object objVal2 = objVals2[i]; + d = objVal1.getClass().getName() + .compareTo(objVal2.getClass().getName()); + if (d == 0) { + if (objVal1 instanceof Type) { + d = compareType((Type) objVal1, (Type) objVal2); + } else if (objVal1 instanceof Handle) { + d = compareHandle((Handle) objVal1, + (Handle) objVal2); + } else { + d = ((Comparable) objVal1).compareTo(objVal2); + } + } + + if (d != 0) { + return d; + } + } + } + return 0; + } + + private static int getSort(final Constant c) { + switch (c.type) { + case 'I': + return 0; + case 'J': + return 1; + case 'F': + return 2; + case 'D': + return 3; + case 's': + return 4; + case 'S': + return 5; + case 'C': + return 6; + case 'T': + return 7; + case 'G': + return 8; + case 'M': + return 9; + case 'N': + return 10; + case 'y': + return 11; + case 't': + return 12; + default: + return 100 + c.type - 'h'; + } + } + } +} diff --git a/src/java/nginx/clojure/asm/optimizer/jdk1.2.2_017.txt.gz b/src/java/nginx/clojure/asm/optimizer/jdk1.2.2_017.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..39cdf9d911f227b374c93ab1e7d59daab8c0b582 GIT binary patch literal 113814 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

    l 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

    1Vek5D+eN)2urd*Q? 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 R7u2@S{{T6jnLV8u5CFwtkIn!9 literal 0 HcmV?d00001 diff --git a/src/java/nginx/clojure/asm/optimizer/shrink-annotations.properties b/src/java/nginx/clojure/asm/optimizer/shrink-annotations.properties new file mode 100644 index 00000000..03fec2e1 --- /dev/null +++ b/src/java/nginx/clojure/asm/optimizer/shrink-annotations.properties @@ -0,0 +1,53 @@ +############################################################################### +#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. +############################################################################### + +# class mappings + +org/objectweb/asm/AnnotationWriter/remove=true + +# field mappings + +org/objectweb/asm/ClassWriter.anns=- +org/objectweb/asm/ClassWriter.ianns=- + +org/objectweb/asm/FieldWriter.anns=- +org/objectweb/asm/FieldWriter.ianns=- + +org/objectweb/asm/MethodWriter.annd=- +org/objectweb/asm/MethodWriter.anns=- +org/objectweb/asm/MethodWriter.ianns=- +org/objectweb/asm/MethodWriter.panns=- +org/objectweb/asm/MethodWriter.ipanns=- + +# method mappings + +org/objectweb/asm/ClassReader.readAnnotationValue(I[CLjava/lang/String;Lorg/objectweb/asm/AnnotationVisitor;)I=- +org/objectweb/asm/ClassReader.readAnnotationValues(I[CZLorg/objectweb/asm/AnnotationVisitor;)I=- +org/objectweb/asm/ClassReader.readParameterAnnotations(I[CZLorg/objectweb/asm/MethodVisitor;)V=- diff --git a/src/java/nginx/clojure/asm/optimizer/shrink-frames.properties b/src/java/nginx/clojure/asm/optimizer/shrink-frames.properties new file mode 100644 index 00000000..ecf580f0 --- /dev/null +++ b/src/java/nginx/clojure/asm/optimizer/shrink-frames.properties @@ -0,0 +1,62 @@ +############################################################################### +#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. +############################################################################### + +# class mappings + +org/objectweb/asm/Frame/remove=true + +# field mappings + +org/objectweb/asm/ClassWriter.typeCount=- +org/objectweb/asm/ClassWriter.typeTable=- + +org/objectweb/asm/Label.frame=- + +org/objectweb/asm/MethodWriter.frameCount=- +org/objectweb/asm/MethodWriter.stackMap=- +org/objectweb/asm/MethodWriter.previousFrameOffset=- +org/objectweb/asm/MethodWriter.previousFrame=- +org/objectweb/asm/MethodWriter.frameIndex=- +org/objectweb/asm/MethodWriter.frame=- + +# method mappings + +org/objectweb/asm/ClassReader.readFrameType([Ljava/lang/Object;II[C[Lorg/objectweb/asm/Label;)I=- + +org/objectweb/asm/ClassWriter.addType(Ljava/lang/String;)I=- +org/objectweb/asm/ClassWriter.addUninitializedType(Ljava/lang/String;I)I=- +org/objectweb/asm/ClassWriter.addType(Lorg/objectweb/asm/Item;)Lorg/objectweb/asm/Item;=- +org/objectweb/asm/ClassWriter.getMergedType(II)I=- + +org/objectweb/asm/MethodWriter.startFrame(III)V=- +org/objectweb/asm/MethodWriter.endFrame()V=- +org/objectweb/asm/MethodWriter.writeFrame()V=- +org/objectweb/asm/MethodWriter.writeFrameTypes(II)V=- +org/objectweb/asm/MethodWriter.writeFrameType(Ljava/lang/Object;)V=- diff --git a/src/java/nginx/clojure/asm/optimizer/shrink-resize.properties b/src/java/nginx/clojure/asm/optimizer/shrink-resize.properties new file mode 100644 index 00000000..97f7e34f --- /dev/null +++ b/src/java/nginx/clojure/asm/optimizer/shrink-resize.properties @@ -0,0 +1,37 @@ +############################################################################### +#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. +############################################################################### + +# class mappings + +# field mappings + +# method mappings + +org/objectweb/asm/MethodWriter.resizeInstructions()V=- \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/optimizer/shrink-signatures.properties b/src/java/nginx/clojure/asm/optimizer/shrink-signatures.properties new file mode 100644 index 00000000..6a486234 --- /dev/null +++ b/src/java/nginx/clojure/asm/optimizer/shrink-signatures.properties @@ -0,0 +1,43 @@ +############################################################################### +#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. +############################################################################### + +# class mappings + +org/objectweb/asm/signature/SignatureReader/remove=true +org/objectweb/asm/signature/SignatureVisitor/remove=true +org/objectweb/asm/signature/SignatureWriter/remove=true + +# field mappings + +org/objectweb/asm/ClassWriter.signature=- + +org/objectweb/asm/FieldWriter.signature=- + +org/objectweb/asm/MethodWriter.signature=- diff --git a/src/java/nginx/clojure/asm/optimizer/shrink-writer.properties b/src/java/nginx/clojure/asm/optimizer/shrink-writer.properties new file mode 100644 index 00000000..1c83ca21 --- /dev/null +++ b/src/java/nginx/clojure/asm/optimizer/shrink-writer.properties @@ -0,0 +1,66 @@ +############################################################################### +#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. +############################################################################### + +# class mappings + +org/objectweb/asm/AnnotationWriter/remove=true +org/objectweb/asm/ByteVector/remove=true +org/objectweb/asm/ClassWriter/remove=true +org/objectweb/asm/Edge/remove=true +org/objectweb/asm/FieldWriter/remove=true +org/objectweb/asm/Frame/remove=true +org/objectweb/asm/Handler/remove=true +org/objectweb/asm/Item/remove=true +org/objectweb/asm/MethodWriter/remove=true + +# field mappings + +org/objectweb/asm/Label.position=- +org/objectweb/asm/Label.referenceCount=- +org/objectweb/asm/Label.srcAndRefPositions=- +org/objectweb/asm/Label.inputStackTop=- +org/objectweb/asm/Label.outputStackMax=- +org/objectweb/asm/Label.frame=- +org/objectweb/asm/Label.successor=- +org/objectweb/asm/Label.successors=- +org/objectweb/asm/Label.next=- + +# method mappings + +org/objectweb/asm/ClassReader.copyPool(Lorg/objectweb/asm/ClassWriter;)V=- + +org/objectweb/asm/Label.addReference(II)V=- +org/objectweb/asm/Label.put(Lorg/objectweb/asm/MethodWriter;Lorg/objectweb/asm/ByteVector;IZ)V=- +org/objectweb/asm/Label.resolve(Lorg/objectweb/asm/MethodWriter;I[B)Z=- +org/objectweb/asm/Label.getFirst()Lorg/objectweb/asm/Label;=- +org/objectweb/asm/Label.inSubroutine(J)Z=- +org/objectweb/asm/Label.inSameSubroutine(Lorg/objectweb/asm/Label;)Z=- +org/objectweb/asm/Label.addToSubroutine(JI)V=- +org/objectweb/asm/Label.visitSubroutine(Lorg/objectweb/asm/Label;JI)V=- diff --git a/src/java/nginx/clojure/asm/optimizer/shrink.properties b/src/java/nginx/clojure/asm/optimizer/shrink.properties new file mode 100644 index 00000000..f7caece5 --- /dev/null +++ b/src/java/nginx/clojure/asm/optimizer/shrink.properties @@ -0,0 +1,379 @@ +############################################################################### +#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. +############################################################################### + +# class mappings + +#org/objectweb/asm/Edge=org/objectweb/asm/a +#org/objectweb/asm/Item=org/objectweb/asm/b +#org/objectweb/asm/FieldWriter=org/objectweb/asm/c +#org/objectweb/asm/MethodWriter=org/objectweb/asm/d +#org/objectweb/asm/AnnotationWriter=org/objectweb/asm/e +#org/objectweb/asm/Context=org/objectweb/asm/f + +java/lang/StringBuilder=java/lang/StringBuffer + + +# field mappings + +org/objectweb/asm/AnnotationWriter.cw=a +org/objectweb/asm/AnnotationWriter.size=b +org/objectweb/asm/AnnotationWriter.named=c +org/objectweb/asm/AnnotationWriter.bv=d +org/objectweb/asm/AnnotationWriter.parent=e +org/objectweb/asm/AnnotationWriter.offset=f +org/objectweb/asm/AnnotationWriter.next=g +org/objectweb/asm/AnnotationWriter.prev=h + +org/objectweb/asm/Attribute.next=a +org/objectweb/asm/Attribute.value=b + +org/objectweb/asm/ByteVector.data=a +org/objectweb/asm/ByteVector.length=b + +org/objectweb/asm/ClassReader.items=a +org/objectweb/asm/ClassReader.strings=c +org/objectweb/asm/ClassReader.maxStringLength=d +#org/objectweb/asm/ClassReader.header=e + +org/objectweb/asm/Context.attrs=a +org/objectweb/asm/Context.flags=b +org/objectweb/asm/Context.buffer=c +org/objectweb/asm/Context.bootstrapMethods=d +org/objectweb/asm/Context.access=e +org/objectweb/asm/Context.name=f +org/objectweb/asm/Context.desc=g +org/objectweb/asm/Context.labels=h +org/objectweb/asm/Context.typeRef=i +org/objectweb/asm/Context.typePath=j +org/objectweb/asm/Context.offset=k +org/objectweb/asm/Context.start=l +org/objectweb/asm/Context.end=m +org/objectweb/asm/Context.index=n +org/objectweb/asm/Context.offset=o +org/objectweb/asm/Context.mode=p +org/objectweb/asm/Context.localCount=q +org/objectweb/asm/Context.localDiff=r +org/objectweb/asm/Context.local=s +org/objectweb/asm/Context.stackCount=t +org/objectweb/asm/Context.stack=u + +org/objectweb/asm/ClassWriter.TYPE=a +org/objectweb/asm/ClassWriter.version=b +org/objectweb/asm/ClassWriter.index=c +org/objectweb/asm/ClassWriter.pool=d +org/objectweb/asm/ClassWriter.items=e +org/objectweb/asm/ClassWriter.threshold=f +org/objectweb/asm/ClassWriter.key=g +org/objectweb/asm/ClassWriter.key2=h +org/objectweb/asm/ClassWriter.key3=i +org/objectweb/asm/ClassWriter.key4=j +org/objectweb/asm/ClassWriter.access=k +org/objectweb/asm/ClassWriter.name=l +org/objectweb/asm/ClassWriter.signature=m +org/objectweb/asm/ClassWriter.superName=n +org/objectweb/asm/ClassWriter.interfaceCount=o +org/objectweb/asm/ClassWriter.interfaces=p +org/objectweb/asm/ClassWriter.sourceFile=q +org/objectweb/asm/ClassWriter.sourceDebug=r +org/objectweb/asm/ClassWriter.enclosingMethodOwner=s +org/objectweb/asm/ClassWriter.enclosingMethod=t +org/objectweb/asm/ClassWriter.anns=u +org/objectweb/asm/ClassWriter.ianns=v +org/objectweb/asm/ClassWriter.tanns=N +org/objectweb/asm/ClassWriter.itanns=O +org/objectweb/asm/ClassWriter.attrs=w +org/objectweb/asm/ClassWriter.innerClassesCount=x +org/objectweb/asm/ClassWriter.innerClasses=y +org/objectweb/asm/ClassWriter.bootstrapMethodsCount=z +org/objectweb/asm/ClassWriter.bootstrapMethods=A +org/objectweb/asm/ClassWriter.firstField=B +org/objectweb/asm/ClassWriter.lastField=C +org/objectweb/asm/ClassWriter.firstMethod=D +org/objectweb/asm/ClassWriter.lastMethod=E +org/objectweb/asm/ClassWriter.compute=F +org/objectweb/asm/ClassWriter.typeCount=G +org/objectweb/asm/ClassWriter.typeTable=H +org/objectweb/asm/ClassWriter.thisName=I +org/objectweb/asm/ClassWriter.hasAsmInsns=J +org/objectweb/asm/ClassWriter.cr=K + +org/objectweb/asm/Edge.info=a +org/objectweb/asm/Edge.successor=b +org/objectweb/asm/Edge.next=c + +org/objectweb/asm/Handler.start=a +org/objectweb/asm/Handler.end=b +org/objectweb/asm/Handler.handler=c +org/objectweb/asm/Handler.desc=d +org/objectweb/asm/Handler.type=e +org/objectweb/asm/Handler.next=f + +org/objectweb/asm/FieldWriter.cw=b +org/objectweb/asm/FieldWriter.access=c +org/objectweb/asm/FieldWriter.name=d +org/objectweb/asm/FieldWriter.desc=e +org/objectweb/asm/FieldWriter.signature=f +org/objectweb/asm/FieldWriter.value=g +org/objectweb/asm/FieldWriter.anns=h +org/objectweb/asm/FieldWriter.ianns=i +org/objectweb/asm/FieldWriter.tanns=k +org/objectweb/asm/FieldWriter.itanns=l +org/objectweb/asm/FieldWriter.attrs=j + +org/objectweb/asm/Item.index=a +org/objectweb/asm/Item.type=b +org/objectweb/asm/Item.intVal=c +org/objectweb/asm/Item.longVal=d +org/objectweb/asm/Item.strVal1=g +org/objectweb/asm/Item.strVal2=h +org/objectweb/asm/Item.strVal3=i +org/objectweb/asm/Item.hashCode=j +org/objectweb/asm/Item.next=k + +org/objectweb/asm/Label.status=a +org/objectweb/asm/Label.line=b +org/objectweb/asm/Label.position=c +org/objectweb/asm/Label.referenceCount=d +org/objectweb/asm/Label.srcAndRefPositions=e +org/objectweb/asm/Label.inputStackTop=f +org/objectweb/asm/Label.outputStackMax=g +org/objectweb/asm/Label.frame=h +org/objectweb/asm/Label.successor=i +org/objectweb/asm/Label.successors=j +org/objectweb/asm/Label.next=k + +org/objectweb/asm/Frame.SIZE=a +org/objectweb/asm/Frame.owner=b +org/objectweb/asm/CurrentFrame.owner=b +org/objectweb/asm/Frame.inputLocals=c +org/objectweb/asm/Frame.inputStack=d +org/objectweb/asm/Frame.outputLocals=e +org/objectweb/asm/Frame.outputStack=f +org/objectweb/asm/Frame.outputStackTop=g +org/objectweb/asm/Frame.initializationCount=h +org/objectweb/asm/Frame.initializations=i + +org/objectweb/asm/MethodWriter.cw=b +org/objectweb/asm/MethodWriter.access=c +org/objectweb/asm/MethodWriter.name=d +org/objectweb/asm/MethodWriter.desc=e +org/objectweb/asm/MethodWriter.descriptor=f +org/objectweb/asm/MethodWriter.signature=g +org/objectweb/asm/MethodWriter.classReaderOffset=h +org/objectweb/asm/MethodWriter.classReaderLength=i +org/objectweb/asm/MethodWriter.exceptionCount=j +org/objectweb/asm/MethodWriter.exceptions=k +org/objectweb/asm/MethodWriter.annd=l +org/objectweb/asm/MethodWriter.anns=m +org/objectweb/asm/MethodWriter.ianns=n +org/objectweb/asm/MethodWriter.tanns=U +org/objectweb/asm/MethodWriter.itanns=V +org/objectweb/asm/MethodWriter.panns=o +org/objectweb/asm/MethodWriter.ipanns=p +org/objectweb/asm/MethodWriter.attrs=q +org/objectweb/asm/MethodWriter.code=r +org/objectweb/asm/MethodWriter.maxStack=s +org/objectweb/asm/MethodWriter.maxLocals=t +org/objectweb/asm/MethodWriter.currentLocals=T +org/objectweb/asm/MethodWriter.frameCount=u +org/objectweb/asm/MethodWriter.stackMap=v +org/objectweb/asm/MethodWriter.previousFrameOffset=w +org/objectweb/asm/MethodWriter.previousFrame=x +#org/objectweb/asm/MethodWriter.frameIndex=y +org/objectweb/asm/MethodWriter.frame=z +org/objectweb/asm/MethodWriter.handlerCount=A +org/objectweb/asm/MethodWriter.firstHandler=B +org/objectweb/asm/MethodWriter.lastHandler=C +org/objectweb/asm/MethodWriter.methodParametersCount=Z +org/objectweb/asm/MethodWriter.methodParameters=$ +org/objectweb/asm/MethodWriter.localVarCount=D +org/objectweb/asm/MethodWriter.localVar=E +org/objectweb/asm/MethodWriter.localVarTypeCount=F +org/objectweb/asm/MethodWriter.localVarType=G +org/objectweb/asm/MethodWriter.lineNumberCount=H +org/objectweb/asm/MethodWriter.lineNumber=I +org/objectweb/asm/MethodWriter.lastCodeOffset=Y +org/objectweb/asm/MethodWriter.ctanns=W +org/objectweb/asm/MethodWriter.ictanns=X +org/objectweb/asm/MethodWriter.cattrs=J +org/objectweb/asm/MethodWriter.subroutines=K +org/objectweb/asm/MethodWriter.compute=L +org/objectweb/asm/MethodWriter.labels=M +org/objectweb/asm/MethodWriter.previousBlock=N +org/objectweb/asm/MethodWriter.currentBlock=O +org/objectweb/asm/MethodWriter.stackSize=P +org/objectweb/asm/MethodWriter.maxStackSize=Q +org/objectweb/asm/MethodWriter.synthetics=R + +org/objectweb/asm/Type.sort=a +org/objectweb/asm/Type.buf=b +org/objectweb/asm/Type.off=c +org/objectweb/asm/Type.len=d + +org/objectweb/asm/TypeReference.value=a + +org/objectweb/asm/TypePath.b=a +org/objectweb/asm/TypePath.offset=b + +org/objectweb/asm/Handle.tag=a +org/objectweb/asm/Handle.owner=b +org/objectweb/asm/Handle.name=c +org/objectweb/asm/Handle.desc=d +org/objectweb/asm/Handle.itf=e + +org/objectweb/asm/signature/SignatureReader.signature=a + +org/objectweb/asm/signature/SignatureWriter.buf=a +org/objectweb/asm/signature/SignatureWriter.hasFormals=b +org/objectweb/asm/signature/SignatureWriter.hasParameters=c +org/objectweb/asm/signature/SignatureWriter.argumentStack=d + +# method mappings + +org/objectweb/asm/AnnotationWriter.getSize()I=a +org/objectweb/asm/AnnotationWriter.put([Lorg/objectweb/asm/AnnotationWriter;ILorg/objectweb/asm/ByteVector;)V=a +org/objectweb/asm/AnnotationWriter.put(Lorg/objectweb/asm/ByteVector;)V=a +org/objectweb/asm/AnnotationWriter.putTarget(ILorg/objectweb/asm/TypePath;Lorg/objectweb/asm/ByteVector;)V=a + +org/objectweb/asm/Attribute.getCount()I=a +org/objectweb/asm/Attribute.getSize(Lorg/objectweb/asm/ClassWriter;[BIII)I=a +org/objectweb/asm/Attribute.put(Lorg/objectweb/asm/ClassWriter;[BIIILorg/objectweb/asm/ByteVector;)V=a + +org/objectweb/asm/ByteVector.enlarge(I)V=a +org/objectweb/asm/ByteVector.put11(II)Lorg/objectweb/asm/ByteVector;=a +org/objectweb/asm/ByteVector.put12(II)Lorg/objectweb/asm/ByteVector;=b +org/objectweb/asm/ByteVector.encodeUTF8(Ljava/lang/String;II)Lorg/objectweb/asm/ByteVector;=c + +org/objectweb/asm/ClassReader.copyPool(Lorg/objectweb/asm/ClassWriter;)V=a +org/objectweb/asm/ClassReader.copyBootstrapMethods(Lorg/objectweb/asm/ClassWriter;[Lorg/objectweb/asm/Item;[C)V=a +org/objectweb/asm/ClassReader.readField(Lorg/objectweb/asm/ClassVisitor;Lorg/objectweb/asm/Context;I)I=a +org/objectweb/asm/ClassReader.readMethod(Lorg/objectweb/asm/ClassVisitor;Lorg/objectweb/asm/Context;I)I=b +org/objectweb/asm/ClassReader.readCode(Lorg/objectweb/asm/MethodVisitor;Lorg/objectweb/asm/Context;I)V=a +org/objectweb/asm/ClassReader.readTypeAnnotations(Lorg/objectweb/asm/MethodVisitor;Lorg/objectweb/asm/Context;IZ)[I=a +org/objectweb/asm/ClassReader.readAnnotationTarget(Lorg/objectweb/asm/Context;I)I=a +org/objectweb/asm/ClassReader.readAnnotationValues(I[CZLorg/objectweb/asm/AnnotationVisitor;)I=a +org/objectweb/asm/ClassReader.readAnnotationValue(I[CLjava/lang/String;Lorg/objectweb/asm/AnnotationVisitor;)I=a +org/objectweb/asm/ClassReader.getAttributes()I=a +org/objectweb/asm/ClassReader.readAttribute([Lorg/objectweb/asm/Attribute;Ljava/lang/String;II[CI[Lorg/objectweb/asm/Label;)Lorg/objectweb/asm/Attribute;=a +org/objectweb/asm/ClassReader.readClass(Ljava/io/InputStream;Z)[B=a +org/objectweb/asm/ClassReader.readParameterAnnotations(Lorg/objectweb/asm/MethodVisitor;Lorg/objectweb/asm/Context;IZ)V=b +org/objectweb/asm/ClassReader.readUTF(II[C)Ljava/lang/String;=a +org/objectweb/asm/ClassReader.getImplicitFrame(Lorg/objectweb/asm/Context;)V=a +org/objectweb/asm/ClassReader.readFrame(IZZLorg/objectweb/asm/Context;)I=a +org/objectweb/asm/ClassReader.readFrameType([Ljava/lang/Object;II[C[Lorg/objectweb/asm/Label;)I=a + +org/objectweb/asm/ClassWriter.get(Lorg/objectweb/asm/Item;)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newClassItem(Ljava/lang/String;)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newConstItem(Ljava/lang/Object;)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newDouble(D)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newFloat(F)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newInteger(I)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newLong(J)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newMethodItem(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newString(Ljava/lang/String;)Lorg/objectweb/asm/Item;=b +org/objectweb/asm/ClassWriter.put122(III)V=a +org/objectweb/asm/ClassWriter.put112(III)V=b +org/objectweb/asm/ClassWriter.put(Lorg/objectweb/asm/Item;)V=b +org/objectweb/asm/ClassWriter.newFieldItem(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.addType(Ljava/lang/String;)I=c +org/objectweb/asm/ClassWriter.addUninitializedType(Ljava/lang/String;I)I=a +org/objectweb/asm/ClassWriter.addType(Lorg/objectweb/asm/Item;)Lorg/objectweb/asm/Item;=c +org/objectweb/asm/ClassWriter.getMergedType(II)I=a +org/objectweb/asm/ClassWriter.newNameTypeItem(Ljava/lang/String;Ljava/lang/String;)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newMethodTypeItem(Ljava/lang/String;)Lorg/objectweb/asm/Item;=c +org/objectweb/asm/ClassWriter.newHandleItem(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Lorg/objectweb/asm/Item;=a +org/objectweb/asm/ClassWriter.newInvokeDynamicItem(Ljava/lang/String;Ljava/lang/String;Lorg/objectweb/asm/Handle;[Ljava/lang/Object;)Lorg/objectweb/asm/Item;=a + +org/objectweb/asm/FieldWriter.getSize()I=a +org/objectweb/asm/FieldWriter.put(Lorg/objectweb/asm/ByteVector;)V=a + +org/objectweb/asm/Frame.get(I)I=a +org/objectweb/asm/Frame.set(II)V=a +org/objectweb/asm/Frame.set(Lorg/objectweb/asm/Frame;)V=b +org/objectweb/asm/CurrentFrame.set(Lorg/objectweb/asm/Frame;)V=b +org/objectweb/asm/Frame.set(Lorg/objectweb/asm/ClassWriter;I[Ljava/lang/Object;I[Ljava/lang/Object;)V=a +org/objectweb/asm/Frame.convert(Lorg/objectweb/asm/ClassWriter;I[Ljava/lang/Object;[I)I=a +org/objectweb/asm/Frame.push(I)V=b +org/objectweb/asm/Frame.push(Lorg/objectweb/asm/ClassWriter;Ljava/lang/String;)V=a +org/objectweb/asm/Frame.type(Lorg/objectweb/asm/ClassWriter;Ljava/lang/String;)I=b +org/objectweb/asm/Frame.pop()I=a +org/objectweb/asm/Frame.pop(Ljava/lang/String;)V=a +org/objectweb/asm/Frame.pop(I)V=c +org/objectweb/asm/Frame.init(I)V=d +org/objectweb/asm/Frame.init(Lorg/objectweb/asm/ClassWriter;I)I=a +org/objectweb/asm/Frame.initInputFrame(Lorg/objectweb/asm/ClassWriter;I[Lorg/objectweb/asm/Type;I)V=a +org/objectweb/asm/Frame.execute(IILorg/objectweb/asm/ClassWriter;Lorg/objectweb/asm/Item;)V=a +org/objectweb/asm/CurrentFrame.execute(IILorg/objectweb/asm/ClassWriter;Lorg/objectweb/asm/Item;)V=a +org/objectweb/asm/Frame.merge(Lorg/objectweb/asm/ClassWriter;Lorg/objectweb/asm/Frame;I)Z=a +org/objectweb/asm/CurrentFrame.merge(Lorg/objectweb/asm/ClassWriter;Lorg/objectweb/asm/Frame;I)Z=a +org/objectweb/asm/Frame.merge(Lorg/objectweb/asm/ClassWriter;I[II)Z=a + +org/objectweb/asm/Handler.remove(Lorg/objectweb/asm/Handler;Lorg/objectweb/asm/Label;Lorg/objectweb/asm/Label;)Lorg/objectweb/asm/Handler;=a + +org/objectweb/asm/Item.isEqualTo(Lorg/objectweb/asm/Item;)Z=a +org/objectweb/asm/Item.set(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V=a +org/objectweb/asm/Item.set(D)V=a +org/objectweb/asm/Item.set(F)V=a +org/objectweb/asm/Item.set(I)V=a +org/objectweb/asm/Item.set(J)V=a +org/objectweb/asm/Item.set(Ljava/lang/String;Ljava/lang/String;I)V=a +org/objectweb/asm/Item.set(II)V=a + +org/objectweb/asm/Label.addReference(II)V=a +org/objectweb/asm/Label.put(Lorg/objectweb/asm/MethodWriter;Lorg/objectweb/asm/ByteVector;IZ)V=a +org/objectweb/asm/Label.resolve(Lorg/objectweb/asm/MethodWriter;I[B)Z=a +org/objectweb/asm/Label.getFirst()Lorg/objectweb/asm/Label;=a +org/objectweb/asm/Label.inSubroutine(J)Z=a +org/objectweb/asm/Label.inSameSubroutine(Lorg/objectweb/asm/Label;)Z=a +org/objectweb/asm/Label.addToSubroutine(JI)V=a +org/objectweb/asm/Label.visitSubroutine(Lorg/objectweb/asm/Label;JI)V=b + +org/objectweb/asm/MethodWriter.visitSwitchInsn(Lorg/objectweb/asm/Label;[Lorg/objectweb/asm/Label;)V=a +org/objectweb/asm/MethodWriter.addSuccessor(ILorg/objectweb/asm/Label;)V=a +org/objectweb/asm/MethodWriter.getSize()I=a +org/objectweb/asm/MethodWriter.put(Lorg/objectweb/asm/ByteVector;)V=a +org/objectweb/asm/MethodWriter.visitFrame(Lorg/objectweb/asm/Frame;)V=b +org/objectweb/asm/MethodWriter.visitImplicitFirstFrame()V=f +org/objectweb/asm/MethodWriter.startFrame(III)I=a +org/objectweb/asm/MethodWriter.endFrame()V=b +org/objectweb/asm/MethodWriter.writeFrame()V=c +org/objectweb/asm/MethodWriter.noSuccessor()V=e +org/objectweb/asm/MethodWriter.writeFrameTypes(II)V=a +org/objectweb/asm/MethodWriter.writeFrameType(Ljava/lang/Object;)V=a + +org/objectweb/asm/Type.getType([CI)Lorg/objectweb/asm/Type;=a +org/objectweb/asm/Type.getDescriptor(Ljava/lang/StringBuilder;)V=a +org/objectweb/asm/Type.getDescriptor(Ljava/lang/StringBuilder;Ljava/lang/Class;)V=a + +org/objectweb/asm/signature/SignatureReader.parseType(Ljava/lang/String;ILorg/objectweb/asm/signature/SignatureVisitor;)I=a + +org/objectweb/asm/signature/SignatureWriter.endFormals()V=a +org/objectweb/asm/signature/SignatureWriter.endArguments()V=b + \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/package.html b/src/java/nginx/clojure/asm/package.html new file mode 100644 index 00000000..ff1e1969 --- /dev/null +++ b/src/java/nginx/clojure/asm/package.html @@ -0,0 +1,87 @@ + + + +Provides a small and fast bytecode manipulation framework. + +

    +In addition to these main abstract classes, ASM provides a {@link +nginx.clojure.asm.ClassReader ClassReader} class, that can parse an +existing class and make a given visitor visit it. ASM also provides +a {@link nginx.clojure.asm.ClassWriter ClassWriter} class, which is +a visitor that generates Java class files. + +

    +In order to generate a class from scratch, only the {@link +nginx.clojure.asm.ClassWriter ClassWriter} class is necessary. Indeed, +in order to generate a class, one must just call its visitXxx +methods with the appropriate arguments to generate the desired fields +and methods. See the "helloworld" example in the ASM distribution for +more details about class generation. + +

    +In order to modify existing classes, one must use a {@link +nginx.clojure.asm.ClassReader ClassReader} class to analyze +the original class, a class modifier, and a {@link nginx.clojure.asm.ClassWriter +ClassWriter} to construct the modified class. The class modifier +is just a {@link nginx.clojure.asm.ClassVisitor ClassVisitor} +that delegates most of the work to another {@link nginx.clojure.asm.ClassVisitor +ClassVisitor}, but that sometimes changes some parameter values, +or call additional methods, in order to implement the desired +modification process. In order to make it easier to implement such +class modifiers, the {@link nginx.clojure.asm.ClassVisitor +ClassVisitor} and {@link nginx.clojure.asm.MethodVisitor MethodVisitor} +classes delegate by default all the method calls they receive to an +optional visitor. See the "adapt" example in the ASM +distribution for more details about class modification. + +

    +The size of the core ASM library, asm.jar, is only 45KB, which is much +smaller than the size of the +BCEL library (504KB), and than the +size of the +SERP library (150KB). ASM is also +much faster than these tools. Indeed the overhead of a load time class +transformation process is of the order of 60% with ASM, 700% or more with BCEL, +and 1100% or more with SERP (see the test/perf directory in the ASM +distribution)! + +@since ASM 1.3 + + diff --git a/src/java/nginx/clojure/asm/signature/SignatureReader.java b/src/java/nginx/clojure/asm/signature/SignatureReader.java index 1186938c..d66ae2c8 100644 --- a/src/java/nginx/clojure/asm/signature/SignatureReader.java +++ b/src/java/nginx/clojure/asm/signature/SignatureReader.java @@ -60,10 +60,10 @@ public SignatureReader(final String signature) { * constructor (see {@link #SignatureReader(String) SignatureReader}). This * method is intended to be called on a {@link SignatureReader} that was * created using a ClassSignature (such as the signature - * parameter of the {@link org.objectweb.asm.ClassVisitor#visit + * parameter of the {@link nginx.clojure.asm.ClassVisitor#visit * ClassVisitor.visit} method) or a MethodTypeSignature (such as the * signature parameter of the - * {@link org.objectweb.asm.ClassVisitor#visitMethod + * {@link nginx.clojure.asm.ClassVisitor#visitMethod * ClassVisitor.visitMethod} method). * * @param v @@ -119,8 +119,8 @@ public void accept(final SignatureVisitor v) { * method is intended to be called on a {@link SignatureReader} that was * created using a FieldTypeSignature, such as the * signature parameter of the - * {@link org.objectweb.asm.ClassVisitor#visitField ClassVisitor.visitField} - * or {@link org.objectweb.asm.MethodVisitor#visitLocalVariable + * {@link nginx.clojure.asm.ClassVisitor#visitField ClassVisitor.visitField} + * or {@link nginx.clojure.asm.MethodVisitor#visitLocalVariable * MethodVisitor.visitLocalVariable} methods. * * @param v diff --git a/src/java/nginx/clojure/asm/signature/SignatureVisitor.java b/src/java/nginx/clojure/asm/signature/SignatureVisitor.java index 655093b8..01266586 100644 --- a/src/java/nginx/clojure/asm/signature/SignatureVisitor.java +++ b/src/java/nginx/clojure/asm/signature/SignatureVisitor.java @@ -39,7 +39,7 @@ *

      *
    • ClassSignature = ( visitFormalTypeParameter * visitClassBound? visitInterfaceBound* )* ( - * visitSuperClass visitInterface* )
    • + * visitSuperclass visitInterface* ) *
    • MethodSignature = ( visitFormalTypeParameter * visitClassBound? visitInterfaceBound* )* ( * visitParameterType* visitReturnType @@ -73,7 +73,7 @@ public abstract class SignatureVisitor { /** * 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; @@ -82,9 +82,12 @@ public abstract class SignatureVisitor { * * @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 SignatureVisitor(final int api) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { + throw new IllegalArgumentException(); + } this.api = api; } diff --git a/src/java/nginx/clojure/asm/signature/SignatureWriter.java b/src/java/nginx/clojure/asm/signature/SignatureWriter.java index e5fd711b..b67f1117 100644 --- a/src/java/nginx/clojure/asm/signature/SignatureWriter.java +++ b/src/java/nginx/clojure/asm/signature/SignatureWriter.java @@ -40,9 +40,9 @@ public class SignatureWriter extends SignatureVisitor { /** - * Buffer used to construct the signature. + * Builder used to construct the signature. */ - private final StringBuffer buf = new StringBuffer(); + private final StringBuilder buf = new StringBuilder(); /** * Indicates if the signature contains formal type parameters. @@ -66,7 +66,7 @@ public class SignatureWriter extends SignatureVisitor { * Constructs a new {@link SignatureWriter} object. */ public SignatureWriter() { - super(Opcodes.ASM4); + super(Opcodes.ASM5); } // ------------------------------------------------------------------------ diff --git a/src/java/nginx/clojure/asm/signature/package.html b/src/java/nginx/clojure/asm/signature/package.html new file mode 100644 index 00000000..0c07d120 --- /dev/null +++ b/src/java/nginx/clojure/asm/signature/package.html @@ -0,0 +1,36 @@ + + + +Provides support for type signatures. + +@since ASM 2.0 + + diff --git a/src/java/nginx/clojure/asm/tree/AbstractInsnNode.java b/src/java/nginx/clojure/asm/tree/AbstractInsnNode.java index 3920e75d..a0c2eae0 100644 --- a/src/java/nginx/clojure/asm/tree/AbstractInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/AbstractInsnNode.java @@ -29,6 +29,7 @@ */ package nginx.clojure.asm.tree; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -127,6 +128,28 @@ public abstract class AbstractInsnNode { */ protected int opcode; + /** + * The runtime visible type annotations of this instruction. This field is + * only used for real instructions (i.e. not for labels, frames, or line + * number nodes). This list is a list of {@link TypeAnnotationNode} objects. + * May be null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this instruction. This field is + * only used for real instructions (i.e. not for labels, frames, or line + * number nodes). This list is a list of {@link TypeAnnotationNode} objects. + * May be null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * Previous instruction in the list to which this instruction belongs. */ @@ -203,6 +226,29 @@ public AbstractInsnNode getNext() { */ public abstract void accept(final MethodVisitor cv); + /** + * Makes the given visitor visit the annotations of this instruction. + * + * @param mv + * a method visitor. + */ + protected final void acceptAnnotations(final MethodVisitor mv) { + int n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(mv.visitInsnAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(mv.visitInsnAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } + } + /** * Returns a copy of this instruction. * @@ -245,4 +291,36 @@ static LabelNode[] clone(final List labels, } return clones; } + + /** + * Clones the annotations of the given instruction into this instruction. + * + * @param insn + * the source instruction. + * @return this instruction. + */ + protected final AbstractInsnNode cloneAnnotations( + final AbstractInsnNode insn) { + if (insn.visibleTypeAnnotations != null) { + this.visibleTypeAnnotations = new ArrayList(); + for (int i = 0; i < insn.visibleTypeAnnotations.size(); ++i) { + TypeAnnotationNode src = insn.visibleTypeAnnotations.get(i); + TypeAnnotationNode ann = new TypeAnnotationNode(src.typeRef, + src.typePath, src.desc); + src.accept(ann); + this.visibleTypeAnnotations.add(ann); + } + } + if (insn.invisibleTypeAnnotations != null) { + this.invisibleTypeAnnotations = new ArrayList(); + for (int i = 0; i < insn.invisibleTypeAnnotations.size(); ++i) { + TypeAnnotationNode src = insn.invisibleTypeAnnotations.get(i); + TypeAnnotationNode ann = new TypeAnnotationNode(src.typeRef, + src.typePath, src.desc); + src.accept(ann); + this.invisibleTypeAnnotations.add(ann); + } + } + return this; + } } diff --git a/src/java/nginx/clojure/asm/tree/AnnotationNode.java b/src/java/nginx/clojure/asm/tree/AnnotationNode.java index 632225a9..36670323 100644 --- a/src/java/nginx/clojure/asm/tree/AnnotationNode.java +++ b/src/java/nginx/clojure/asm/tree/AnnotationNode.java @@ -36,7 +36,7 @@ import nginx.clojure.asm.Opcodes; /** - * A node that represents an annotationn. + * A node that represents an annotation. * * @author Eric Bruneton */ @@ -52,8 +52,8 @@ public class AnnotationNode extends AnnotationVisitor { * as two consecutive elements in the list. The name is a {@link String}, * and the value may be a {@link Byte}, {@link Boolean}, {@link Character}, * {@link Short}, {@link Integer}, {@link Long}, {@link Float}, - * {@link Double}, {@link String} or {@link nginx.clojure.asm.Type}, or an - * two elements String array (for enumeration values), a + * {@link Double}, {@link String} or {@link nginx.clojure.asm.Type}, or a + * two elements String array (for enumeration values), an * {@link AnnotationNode}, or a {@link List} of values of one of the * preceding types. The list may be null if there is no name value * pair. @@ -67,9 +67,14 @@ public class AnnotationNode extends AnnotationVisitor { * * @param desc * the class descriptor of the annotation class. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public AnnotationNode(final String desc) { - this(Opcodes.ASM4, desc); + this(Opcodes.ASM5, desc); + if (getClass() != AnnotationNode.class) { + throw new IllegalStateException(); + } } /** @@ -77,7 +82,7 @@ public AnnotationNode(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 desc * the class descriptor of the annotation class. */ @@ -93,7 +98,7 @@ public AnnotationNode(final int api, final String desc) { * where the visited values must be stored. */ AnnotationNode(final List values) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); this.values = values; } @@ -109,7 +114,65 @@ public void visit(final String name, final Object value) { if (this.desc != null) { values.add(name); } - values.add(value); + if (value instanceof byte[]) { + byte[] v = (byte[]) value; + ArrayList l = new ArrayList(v.length); + for (byte b : v) { + l.add(b); + } + values.add(l); + } else if (value instanceof boolean[]) { + boolean[] v = (boolean[]) value; + ArrayList l = new ArrayList(v.length); + for (boolean b : v) { + l.add(b); + } + values.add(l); + } else if (value instanceof short[]) { + short[] v = (short[]) value; + ArrayList l = new ArrayList(v.length); + for (short s : v) { + l.add(s); + } + values.add(l); + } else if (value instanceof char[]) { + char[] v = (char[]) value; + ArrayList l = new ArrayList(v.length); + for (char c : v) { + l.add(c); + } + values.add(l); + } else if (value instanceof int[]) { + int[] v = (int[]) value; + ArrayList l = new ArrayList(v.length); + for (int i : v) { + l.add(i); + } + values.add(l); + } else if (value instanceof long[]) { + long[] v = (long[]) value; + ArrayList l = new ArrayList(v.length); + for (long lng : v) { + l.add(lng); + } + values.add(l); + } else if (value instanceof float[]) { + float[] v = (float[]) value; + ArrayList l = new ArrayList(v.length); + for (float f : v) { + l.add(f); + } + values.add(l); + } else if (value instanceof double[]) { + double[] v = (double[]) value; + ArrayList l = new ArrayList(v.length); + for (double d : v) { + l.add(d); + } + values.add(l); + } else { + values.add(value); + } } @Override @@ -166,7 +229,8 @@ public void visitEnd() { * versions of the ASM API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { // nothing to do @@ -212,11 +276,13 @@ static void accept(final AnnotationVisitor av, final String name, an.accept(av.visitAnnotation(name, an.desc)); } else if (value instanceof List) { AnnotationVisitor v = av.visitArray(name); - List array = (List) value; - for (int j = 0; j < array.size(); ++j) { - accept(v, null, array.get(j)); + if (v != null) { + List array = (List) value; + for (int j = 0; j < array.size(); ++j) { + accept(v, null, array.get(j)); + } + v.visitEnd(); } - v.visitEnd(); } else { av.visit(name, value); } diff --git a/src/java/nginx/clojure/asm/tree/ClassNode.java b/src/java/nginx/clojure/asm/tree/ClassNode.java index 0f64f2b3..e5ca0454 100644 --- a/src/java/nginx/clojure/asm/tree/ClassNode.java +++ b/src/java/nginx/clojure/asm/tree/ClassNode.java @@ -39,6 +39,7 @@ import nginx.clojure.asm.FieldVisitor; import nginx.clojure.asm.MethodVisitor; import nginx.clojure.asm.Opcodes; +import nginx.clojure.asm.TypePath; /** * A node that represents a class. @@ -132,6 +133,24 @@ public class ClassNode extends ClassVisitor { */ public List invisibleAnnotations; + /** + * The runtime visible type annotations of this class. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this class. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * The non standard attributes of this class. This list is a list of * {@link Attribute} objects. May be null. @@ -168,9 +187,15 @@ public class ClassNode extends ClassVisitor { * Constructs a new {@link ClassNode}. Subclasses must not use this * constructor. Instead, they must use the {@link #ClassNode(int)} * version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public ClassNode() { - this(Opcodes.ASM4); + this(Opcodes.ASM5); + if (getClass() != ClassNode.class) { + throw new IllegalStateException(); + } } /** @@ -178,7 +203,7 @@ public ClassNode() { * * @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 ClassNode(final int api) { super(api); @@ -238,6 +263,24 @@ public AnnotationVisitor visitAnnotation(final String desc, return an; } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (visibleTypeAnnotations == null) { + visibleTypeAnnotations = new ArrayList(1); + } + visibleTypeAnnotations.add(an); + } else { + if (invisibleTypeAnnotations == null) { + invisibleTypeAnnotations = new ArrayList(1); + } + invisibleTypeAnnotations.add(an); + } + return an; + } + @Override public void visitAttribute(final Attribute attr) { if (attrs == null) { @@ -286,10 +329,26 @@ public void visitEnd() { * API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { - // nothing to do + if (api == Opcodes.ASM4) { + if (visibleTypeAnnotations != null + && visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleTypeAnnotations != null + && invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + for (FieldNode f : fields) { + f.check(api); + } + for (MethodNode m : methods) { + m.check(api); + } + } } /** @@ -323,6 +382,19 @@ public void accept(final ClassVisitor cv) { AnnotationNode an = invisibleAnnotations.get(i); an.accept(cv.visitAnnotation(an.desc, false)); } + n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(cv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(cv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } n = attrs == null ? 0 : attrs.size(); for (i = 0; i < n; ++i) { cv.visitAttribute(attrs.get(i)); diff --git a/src/java/nginx/clojure/asm/tree/FieldInsnNode.java b/src/java/nginx/clojure/asm/tree/FieldInsnNode.java index 94ad13db..af231eb7 100644 --- a/src/java/nginx/clojure/asm/tree/FieldInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/FieldInsnNode.java @@ -97,12 +97,14 @@ public int getType() { } @Override - public void accept(final MethodVisitor cv) { - cv.visitFieldInsn(opcode, owner, name, desc); + public void accept(final MethodVisitor mv) { + mv.visitFieldInsn(opcode, owner, name, desc); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new FieldInsnNode(opcode, owner, name, desc); + return new FieldInsnNode(opcode, owner, name, desc) + .cloneAnnotations(this); } } diff --git a/src/java/nginx/clojure/asm/tree/FieldNode.java b/src/java/nginx/clojure/asm/tree/FieldNode.java index fbebce28..18bbe934 100644 --- a/src/java/nginx/clojure/asm/tree/FieldNode.java +++ b/src/java/nginx/clojure/asm/tree/FieldNode.java @@ -37,6 +37,7 @@ import nginx.clojure.asm.ClassVisitor; import nginx.clojure.asm.FieldVisitor; import nginx.clojure.asm.Opcodes; +import nginx.clojure.asm.TypePath; /** * A node that represents a field. @@ -91,6 +92,24 @@ public class FieldNode extends FieldVisitor { */ public List invisibleAnnotations; + /** + * The runtime visible type annotations of this field. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this field. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * The non standard attributes of this field. This list is a list of * {@link Attribute} objects. May be null. @@ -120,20 +139,24 @@ public class FieldNode extends FieldVisitor { * null if the field does not have an initial value, * must be an {@link Integer}, a {@link Float}, a {@link Long}, a * {@link Double} or a {@link String}. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public FieldNode(final int access, final String name, final String desc, final String signature, final Object value) { - this(Opcodes.ASM4, access, name, desc, signature, value); + this(Opcodes.ASM5, access, name, desc, signature, value); + if (getClass() != FieldNode.class) { + throw new IllegalStateException(); + } } /** * Constructs a new {@link FieldNode}. Subclasses must not use this - * constructor. Instead, they must use the - * {@link #FieldNode(int, int, String, String, String, Object)} version. + * constructor. * * @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 * the field's access flags (see * {@link nginx.clojure.asm.Opcodes}). This parameter also @@ -183,6 +206,24 @@ public AnnotationVisitor visitAnnotation(final String desc, return an; } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (visibleTypeAnnotations == null) { + visibleTypeAnnotations = new ArrayList(1); + } + visibleTypeAnnotations.add(an); + } else { + if (invisibleTypeAnnotations == null) { + invisibleTypeAnnotations = new ArrayList(1); + } + invisibleTypeAnnotations.add(an); + } + return an; + } + @Override public void visitAttribute(final Attribute attr) { if (attrs == null) { @@ -206,10 +247,20 @@ public void visitEnd() { * API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { - // nothing to do + if (api == Opcodes.ASM4) { + if (visibleTypeAnnotations != null + && visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleTypeAnnotations != null + && invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + } } /** @@ -234,6 +285,19 @@ public void accept(final ClassVisitor cv) { AnnotationNode an = invisibleAnnotations.get(i); an.accept(fv.visitAnnotation(an.desc, false)); } + n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(fv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(fv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } n = attrs == null ? 0 : attrs.size(); for (i = 0; i < n; ++i) { fv.visitAttribute(attrs.get(i)); diff --git a/src/java/nginx/clojure/asm/tree/IincInsnNode.java b/src/java/nginx/clojure/asm/tree/IincInsnNode.java index cd0abbf5..67dfe503 100644 --- a/src/java/nginx/clojure/asm/tree/IincInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/IincInsnNode.java @@ -73,10 +73,11 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { mv.visitIincInsn(var, incr); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new IincInsnNode(var, incr); + return new IincInsnNode(var, incr).cloneAnnotations(this); } } \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/tree/InsnList.java b/src/java/nginx/clojure/asm/tree/InsnList.java index 67656301..05431c4c 100644 --- a/src/java/nginx/clojure/asm/tree/InsnList.java +++ b/src/java/nginx/clojure/asm/tree/InsnList.java @@ -100,7 +100,7 @@ public AbstractInsnNode getLast() { * the index of the instruction that must be returned. * @return the instruction whose index is given. * @throws IndexOutOfBoundsException - * if (index < 0 || index >= size()). + * if (index < 0 || index >= size()). */ public AbstractInsnNode get(final int index) { if (index < 0 || index >= size) { @@ -176,6 +176,9 @@ public ListIterator iterator() { /** * Returns an iterator over the instructions in this list. * + * @param index + * index of instruction for the iterator to start at + * * @return an iterator over the instructions in this list. */ @SuppressWarnings("unchecked") @@ -521,12 +524,15 @@ public void resetLabels() { } // this class is not generified because it will create bridges + @SuppressWarnings("rawtypes") private final class InsnListIterator implements ListIterator { AbstractInsnNode next; AbstractInsnNode prev; + AbstractInsnNode remove; + InsnListIterator(int index) { if (index == size()) { next = null; @@ -548,12 +554,22 @@ public Object next() { AbstractInsnNode result = next; prev = result; next = result.next; + remove = result; return result; } public void remove() { - InsnList.this.remove(prev); - prev = prev.prev; + if (remove != null) { + if (remove == next) { + next = next.next; + } else { + prev = prev.prev; + } + InsnList.this.remove(remove); + remove = null; + } else { + throw new IllegalStateException(); + } } public boolean hasPrevious() { @@ -564,6 +580,7 @@ public Object previous() { AbstractInsnNode result = prev; next = result; prev = result.prev; + remove = result; return result; } @@ -588,13 +605,28 @@ public int previousIndex() { } public void add(Object o) { - InsnList.this.insertBefore(next, (AbstractInsnNode) o); + if (next != null) { + InsnList.this.insertBefore(next, (AbstractInsnNode) o); + } else if (prev != null) { + InsnList.this.insert(prev, (AbstractInsnNode) o); + } else { + InsnList.this.add((AbstractInsnNode) o); + } prev = (AbstractInsnNode) o; + remove = null; } public void set(Object o) { - InsnList.this.set(next.prev, (AbstractInsnNode) o); - prev = (AbstractInsnNode) o; + if (remove != null) { + InsnList.this.set(remove, (AbstractInsnNode) o); + if (remove == prev) { + prev = (AbstractInsnNode) o; + } else { + next = (AbstractInsnNode) o; + } + } else { + throw new IllegalStateException(); + } } } } diff --git a/src/java/nginx/clojure/asm/tree/InsnNode.java b/src/java/nginx/clojure/asm/tree/InsnNode.java index e23dded8..495bef67 100644 --- a/src/java/nginx/clojure/asm/tree/InsnNode.java +++ b/src/java/nginx/clojure/asm/tree/InsnNode.java @@ -78,10 +78,11 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { mv.visitInsn(opcode); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new InsnNode(opcode); + return new InsnNode(opcode).cloneAnnotations(this); } } diff --git a/src/java/nginx/clojure/asm/tree/IntInsnNode.java b/src/java/nginx/clojure/asm/tree/IntInsnNode.java index fef8fba1..c3ef1d47 100644 --- a/src/java/nginx/clojure/asm/tree/IntInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/IntInsnNode.java @@ -78,10 +78,11 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { mv.visitIntInsn(opcode, operand); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new IntInsnNode(opcode, operand); + return new IntInsnNode(opcode, operand).cloneAnnotations(this); } } diff --git a/src/java/nginx/clojure/asm/tree/InvokeDynamicInsnNode.java b/src/java/nginx/clojure/asm/tree/InvokeDynamicInsnNode.java index db473eb2..d2883d41 100644 --- a/src/java/nginx/clojure/asm/tree/InvokeDynamicInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/InvokeDynamicInsnNode.java @@ -91,10 +91,12 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs); + return new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs) + .cloneAnnotations(this); } } \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/tree/JumpInsnNode.java b/src/java/nginx/clojure/asm/tree/JumpInsnNode.java index 3fc367d7..170c7e7c 100644 --- a/src/java/nginx/clojure/asm/tree/JumpInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/JumpInsnNode.java @@ -86,10 +86,12 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { mv.visitJumpInsn(opcode, label.getLabel()); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new JumpInsnNode(opcode, clone(label, labels)); + return new JumpInsnNode(opcode, clone(label, labels)) + .cloneAnnotations(this); } } diff --git a/src/java/nginx/clojure/asm/tree/LdcInsnNode.java b/src/java/nginx/clojure/asm/tree/LdcInsnNode.java index 80a47a5c..f33e4c5b 100644 --- a/src/java/nginx/clojure/asm/tree/LdcInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/LdcInsnNode.java @@ -69,10 +69,11 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { mv.visitLdcInsn(cst); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new LdcInsnNode(cst); + return new LdcInsnNode(cst).cloneAnnotations(this); } } \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/tree/LocalVariableAnnotationNode.java b/src/java/nginx/clojure/asm/tree/LocalVariableAnnotationNode.java new file mode 100644 index 00000000..2ef3aa4e --- /dev/null +++ b/src/java/nginx/clojure/asm/tree/LocalVariableAnnotationNode.java @@ -0,0 +1,157 @@ +/*** + * 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.tree; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import nginx.clojure.asm.Label; +import nginx.clojure.asm.MethodVisitor; +import nginx.clojure.asm.Opcodes; +import nginx.clojure.asm.TypePath; +import nginx.clojure.asm.TypeReference; + +/** + * A node that represents a type annotation on a local or resource variable. + * + * @author Eric Bruneton + */ +public class LocalVariableAnnotationNode extends TypeAnnotationNode { + + /** + * The fist instructions corresponding to the continuous ranges that make + * the scope of this local variable (inclusive). Must not be null. + */ + public List start; + + /** + * The last instructions corresponding to the continuous ranges that make + * the scope of this local variable (exclusive). This list must have the + * same size as the 'start' list. Must not be null. + */ + public List end; + + /** + * The local variable's index in each range. This list must have the same + * size as the 'start' list. Must not be null. + */ + public List index; + + /** + * Constructs a new {@link LocalVariableAnnotationNode}. Subclasses must + * not use this constructor. Instead, they must use the + * {@link #LocalVariableAnnotationNode(int, TypePath, LabelNode[], LabelNode[], int[], String)} + * version. + * + * @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 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. + */ + public LocalVariableAnnotationNode(int typeRef, TypePath typePath, + LabelNode[] start, LabelNode[] end, int[] index, String desc) { + this(Opcodes.ASM5, typeRef, typePath, start, end, index, desc); + } + + /** + * Constructs a new {@link LocalVariableAnnotationNode}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @param typeRef + * a reference to the annotated type. See {@link TypeReference}. + * @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 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. + */ + public LocalVariableAnnotationNode(int api, int typeRef, TypePath typePath, + LabelNode[] start, LabelNode[] end, int[] index, String desc) { + super(api, typeRef, typePath, desc); + this.start = new ArrayList(start.length); + this.start.addAll(Arrays.asList(start)); + this.end = new ArrayList(end.length); + this.end.addAll(Arrays.asList(end)); + this.index = new ArrayList(index.length); + for (int i : index) { + this.index.add(i); + } + } + + /** + * Makes the given visitor visit this type annotation. + * + * @param mv + * the visitor that must visit this annotation. + * @param visible + * true if the annotation is visible at runtime. + */ + public void accept(final MethodVisitor mv, boolean visible) { + Label[] start = new Label[this.start.size()]; + Label[] end = new Label[this.end.size()]; + int[] index = new int[this.index.size()]; + for (int i = 0; i < start.length; ++i) { + start[i] = this.start.get(i).getLabel(); + end[i] = this.end.get(i).getLabel(); + index[i] = this.index.get(i); + } + accept(mv.visitLocalVariableAnnotation(typeRef, typePath, start, end, + index, desc, true)); + } +} diff --git a/src/java/nginx/clojure/asm/tree/LookupSwitchInsnNode.java b/src/java/nginx/clojure/asm/tree/LookupSwitchInsnNode.java index 604c4b0f..5701944f 100644 --- a/src/java/nginx/clojure/asm/tree/LookupSwitchInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/LookupSwitchInsnNode.java @@ -81,7 +81,7 @@ public LookupSwitchInsnNode(final LabelNode dflt, final int[] keys, : labels.length); if (keys != null) { for (int i = 0; i < keys.length; ++i) { - this.keys.add(new Integer(keys[i])); + this.keys.add(keys[i]); } } if (labels != null) { @@ -105,6 +105,7 @@ public void accept(final MethodVisitor mv) { labels[i] = this.labels.get(i).getLabel(); } mv.visitLookupSwitchInsn(dflt.getLabel(), keys, labels); + acceptAnnotations(mv); } @Override @@ -112,6 +113,6 @@ public AbstractInsnNode clone(final Map labels) { LookupSwitchInsnNode clone = new LookupSwitchInsnNode(clone(dflt, labels), null, clone(this.labels, labels)); clone.keys.addAll(keys); - return clone; + return clone.cloneAnnotations(this); } } diff --git a/src/java/nginx/clojure/asm/tree/MethodInsnNode.java b/src/java/nginx/clojure/asm/tree/MethodInsnNode.java index da79f0b4..15e22660 100644 --- a/src/java/nginx/clojure/asm/tree/MethodInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/MethodInsnNode.java @@ -32,6 +32,7 @@ import java.util.Map; import nginx.clojure.asm.MethodVisitor; +import nginx.clojure.asm.Opcodes; /** * A node that represents a method instruction. A method instruction is an @@ -57,6 +58,11 @@ public class MethodInsnNode extends AbstractInsnNode { */ public String desc; + /** + * If the method's owner class if an interface. + */ + public boolean itf; + /** * Constructs a new {@link MethodInsnNode}. * @@ -73,12 +79,37 @@ public class MethodInsnNode extends AbstractInsnNode { * @param desc * the method's descriptor (see {@link nginx.clojure.asm.Type}). */ + @Deprecated public MethodInsnNode(final int opcode, final String owner, final String name, final String desc) { + this(opcode, owner, name, desc, opcode == Opcodes.INVOKEINTERFACE); + } + + /** + * Constructs a new {@link MethodInsnNode}. + * + * @param opcode + * the opcode of the type instruction to be constructed. This + * opcode must be INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner + * the internal name of the method's owner class (see + * {@link nginx.clojure.asm.Type#getInternalName() + * getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link nginx.clojure.asm.Type}). + * @param itf + * if the method's owner class is an interface. + */ + public MethodInsnNode(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { super(opcode); this.owner = owner; this.name = name; this.desc = desc; + this.itf = itf; } /** @@ -99,11 +130,12 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { - mv.visitMethodInsn(opcode, owner, name, desc); + mv.visitMethodInsn(opcode, owner, name, desc, itf); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new MethodInsnNode(opcode, owner, name, desc); + return new MethodInsnNode(opcode, owner, name, desc, itf); } } \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/tree/MethodNode.java b/src/java/nginx/clojure/asm/tree/MethodNode.java index e0f47099..139e3e21 100644 --- a/src/java/nginx/clojure/asm/tree/MethodNode.java +++ b/src/java/nginx/clojure/asm/tree/MethodNode.java @@ -41,6 +41,7 @@ import nginx.clojure.asm.MethodVisitor; import nginx.clojure.asm.Opcodes; import nginx.clojure.asm.Type; +import nginx.clojure.asm.TypePath; /** * A node that represents a method. @@ -77,6 +78,11 @@ public class MethodNode extends MethodVisitor { */ public List exceptions; + /** + * The method parameter info (access flags and name) + */ + public List parameters; + /** * The runtime visible annotations of this method. This list is a list of * {@link AnnotationNode} objects. May be null. @@ -95,6 +101,24 @@ public class MethodNode extends MethodVisitor { */ public List invisibleAnnotations; + /** + * The runtime visible type annotations of this method. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this method. This list is a + * list of {@link TypeAnnotationNode} objects. May be null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * The non standard attributes of this method. This list is a list of * {@link Attribute} objects. May be null. @@ -166,6 +190,22 @@ public class MethodNode extends MethodVisitor { */ public List localVariables; + /** + * The visible local variable annotations of this method. This list is a + * list of {@link LocalVariableAnnotationNode} objects. May be null + * + * @associates nginx.clojure.asm.tree.LocalVariableAnnotationNode + */ + public List visibleLocalVariableAnnotations; + + /** + * The invisible local variable annotations of this method. This list is a + * list of {@link LocalVariableAnnotationNode} objects. May be null + * + * @associates nginx.clojure.asm.tree.LocalVariableAnnotationNode + */ + public List invisibleLocalVariableAnnotations; + /** * If the accept method has been called on this object. */ @@ -175,9 +215,15 @@ public class MethodNode extends MethodVisitor { * Constructs an uninitialized {@link MethodNode}. Subclasses must not * use this constructor. Instead, they must use the * {@link #MethodNode(int)} version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public MethodNode() { - this(Opcodes.ASM4); + this(Opcodes.ASM5); + if (getClass() != MethodNode.class) { + throw new IllegalStateException(); + } } /** @@ -185,7 +231,7 @@ public MethodNode() { * * @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 MethodNode(final int api) { super(api); @@ -211,10 +257,15 @@ public MethodNode(final int api) { * 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 MethodNode(final int access, final String name, final String desc, final String signature, final String[] exceptions) { - this(Opcodes.ASM4, access, name, desc, signature, exceptions); + this(Opcodes.ASM5, access, name, desc, signature, exceptions); + if (getClass() != MethodNode.class) { + throw new IllegalStateException(); + } } /** @@ -222,7 +273,7 @@ public MethodNode(final int access, final String name, 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 * the method's access flags (see {@link Opcodes}). This * parameter also indicates if the method is synthetic and/or @@ -263,6 +314,15 @@ public MethodNode(final int api, final int access, final String name, // ------------------------------------------------------------------------ @Override + public void visitParameter(String name, int access) { + if (parameters == null) { + parameters = new ArrayList(5); + } + parameters.add(new ParameterNode(name, access)); + } + + @Override + @SuppressWarnings("serial") public AnnotationVisitor visitAnnotationDefault() { return new AnnotationNode(new ArrayList(0) { @Override @@ -292,6 +352,25 @@ public AnnotationVisitor visitAnnotation(final String desc, } @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (visibleTypeAnnotations == null) { + visibleTypeAnnotations = new ArrayList(1); + } + visibleTypeAnnotations.add(an); + } else { + if (invisibleTypeAnnotations == null) { + invisibleTypeAnnotations = new ArrayList(1); + } + invisibleTypeAnnotations.add(an); + } + return an; + } + + @Override + @SuppressWarnings("unchecked") public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { AnnotationNode an = new AnnotationNode(desc); @@ -365,12 +444,27 @@ public void visitFieldInsn(final int opcode, final String owner, instructions.add(new FieldInsnNode(opcode, owner, name, desc)); } + @Deprecated @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } instructions.add(new MethodInsnNode(opcode, owner, name, desc)); } + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + instructions.add(new MethodInsnNode(opcode, owner, name, desc, itf)); + } + @Override public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { @@ -416,6 +510,33 @@ public void visitMultiANewArrayInsn(final String desc, final int dims) { instructions.add(new MultiANewArrayInsnNode(desc, dims)); } + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + // Finds the last real instruction, i.e. the instruction targeted by + // this annotation. + AbstractInsnNode insn = instructions.getLast(); + while (insn.getOpcode() == -1) { + insn = insn.getPrevious(); + } + // Adds the annotation to this instruction. + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (insn.visibleTypeAnnotations == null) { + insn.visibleTypeAnnotations = new ArrayList( + 1); + } + insn.visibleTypeAnnotations.add(an); + } else { + if (insn.invisibleTypeAnnotations == null) { + insn.invisibleTypeAnnotations = new ArrayList( + 1); + } + insn.invisibleTypeAnnotations.add(an); + } + return an; + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -423,6 +544,27 @@ public void visitTryCatchBlock(final Label start, final Label end, getLabelNode(end), getLabelNode(handler), type)); } + @Override + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TryCatchBlockNode tcb = tryCatchBlocks.get((typeRef & 0x00FFFF00) >> 8); + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (tcb.visibleTypeAnnotations == null) { + tcb.visibleTypeAnnotations = new ArrayList( + 1); + } + tcb.visibleTypeAnnotations.add(an); + } else { + if (tcb.invisibleTypeAnnotations == null) { + tcb.invisibleTypeAnnotations = new ArrayList( + 1); + } + tcb.invisibleTypeAnnotations.add(an); + } + return an; + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -431,6 +573,29 @@ public void visitLocalVariable(final String name, final String desc, getLabelNode(start), getLabelNode(end), index)); } + @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + LocalVariableAnnotationNode an = new LocalVariableAnnotationNode( + typeRef, typePath, getLabelNodes(start), getLabelNodes(end), + index, desc); + if (visible) { + if (visibleLocalVariableAnnotations == null) { + visibleLocalVariableAnnotations = new ArrayList( + 1); + } + visibleLocalVariableAnnotations.add(an); + } else { + if (invisibleLocalVariableAnnotations == null) { + invisibleLocalVariableAnnotations = new ArrayList( + 1); + } + invisibleLocalVariableAnnotations.add(an); + } + return an; + } + @Override public void visitLineNumber(final int line, final Label start) { instructions.add(new LineNumberNode(line, getLabelNode(start))); @@ -494,10 +659,57 @@ private Object[] getLabelNodes(final Object[] objs) { * versions of the ASM API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { - // nothing to do + if (api == Opcodes.ASM4) { + if (visibleTypeAnnotations != null + && visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleTypeAnnotations != null + && invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + int n = tryCatchBlocks == null ? 0 : tryCatchBlocks.size(); + for (int i = 0; i < n; ++i) { + TryCatchBlockNode tcb = tryCatchBlocks.get(i); + if (tcb.visibleTypeAnnotations != null + && tcb.visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (tcb.invisibleTypeAnnotations != null + && tcb.invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + } + for (int i = 0; i < instructions.size(); ++i) { + AbstractInsnNode insn = instructions.get(i); + if (insn.visibleTypeAnnotations != null + && insn.visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (insn.invisibleTypeAnnotations != null + && insn.invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (insn instanceof MethodInsnNode) { + boolean itf = ((MethodInsnNode) insn).itf; + if (itf != (insn.opcode == Opcodes.INVOKEINTERFACE)) { + throw new RuntimeException(); + } + } + } + if (visibleLocalVariableAnnotations != null + && visibleLocalVariableAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleLocalVariableAnnotations != null + && invisibleLocalVariableAnnotations.size() > 0) { + throw new RuntimeException(); + } + } } /** @@ -523,8 +735,14 @@ public void accept(final ClassVisitor cv) { * a method visitor. */ public void accept(final MethodVisitor mv) { - // visits the method attributes + // visits the method parameters int i, j, n; + n = parameters == null ? 0 : parameters.size(); + for (i = 0; i < n; i++) { + ParameterNode parameter = parameters.get(i); + mv.visitParameter(parameter.name, parameter.access); + } + // visits the method attributes if (annotationDefault != null) { AnnotationVisitor av = mv.visitAnnotationDefault(); AnnotationNode.accept(av, null, annotationDefault); @@ -542,6 +760,19 @@ public void accept(final MethodVisitor mv) { AnnotationNode an = invisibleAnnotations.get(i); an.accept(mv.visitAnnotation(an.desc, false)); } + n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(mv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(mv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } n = visibleParameterAnnotations == null ? 0 : visibleParameterAnnotations.length; for (i = 0; i < n; ++i) { @@ -579,6 +810,7 @@ public void accept(final MethodVisitor mv) { // visits try catch blocks n = tryCatchBlocks == null ? 0 : tryCatchBlocks.size(); for (i = 0; i < n; ++i) { + tryCatchBlocks.get(i).updateIndex(i); tryCatchBlocks.get(i).accept(mv); } // visits instructions @@ -588,6 +820,17 @@ public void accept(final MethodVisitor mv) { for (i = 0; i < n; ++i) { localVariables.get(i).accept(mv); } + // visits local variable annotations + n = visibleLocalVariableAnnotations == null ? 0 + : visibleLocalVariableAnnotations.size(); + for (i = 0; i < n; ++i) { + visibleLocalVariableAnnotations.get(i).accept(mv, true); + } + n = invisibleLocalVariableAnnotations == null ? 0 + : invisibleLocalVariableAnnotations.size(); + for (i = 0; i < n; ++i) { + invisibleLocalVariableAnnotations.get(i).accept(mv, false); + } // visits maxs mv.visitMaxs(maxStack, maxLocals); visited = true; diff --git a/src/java/nginx/clojure/asm/tree/MultiANewArrayInsnNode.java b/src/java/nginx/clojure/asm/tree/MultiANewArrayInsnNode.java index 872b1b80..6cf0f02d 100644 --- a/src/java/nginx/clojure/asm/tree/MultiANewArrayInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/MultiANewArrayInsnNode.java @@ -73,11 +73,12 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { mv.visitMultiANewArrayInsn(desc, dims); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new MultiANewArrayInsnNode(desc, dims); + return new MultiANewArrayInsnNode(desc, dims).cloneAnnotations(this); } } \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/tree/ParameterNode.java b/src/java/nginx/clojure/asm/tree/ParameterNode.java new file mode 100644 index 00000000..325ea3e7 --- /dev/null +++ b/src/java/nginx/clojure/asm/tree/ParameterNode.java @@ -0,0 +1,76 @@ +/*** + * 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.tree; + +import nginx.clojure.asm.MethodVisitor; + +/** + * A node that represents a parameter access and name. + * + * @author Remi Forax + */ +public class ParameterNode { + /** + * The parameter's name. + */ + public String name; + + /** + * The parameter's access flags (see {@link nginx.clojure.asm.Opcodes}). + * Valid values are ACC_FINAL, ACC_SYNTHETIC and + * ACC_MANDATED. + */ + public int access; + + /** + * Constructs a new {@link ParameterNode}. + * + * @param access + * The parameter's access flags. Valid values are + * ACC_FINAL, ACC_SYNTHETIC or/and + * ACC_MANDATED (see {@link nginx.clojure.asm.Opcodes}). + * @param name + * the parameter's name. + */ + public ParameterNode(final String name, final int access) { + this.name = name; + this.access = access; + } + + /** + * Makes the given visitor visit this parameter declaration. + * + * @param mv + * a method visitor. + */ + public void accept(final MethodVisitor mv) { + mv.visitParameter(name, access); + } +} diff --git a/src/java/nginx/clojure/asm/tree/TableSwitchInsnNode.java b/src/java/nginx/clojure/asm/tree/TableSwitchInsnNode.java index 1a7a9b3e..f1427702 100644 --- a/src/java/nginx/clojure/asm/tree/TableSwitchInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/TableSwitchInsnNode.java @@ -103,11 +103,12 @@ public void accept(final MethodVisitor mv) { labels[i] = this.labels.get(i).getLabel(); } mv.visitTableSwitchInsn(min, max, dflt.getLabel(), labels); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { return new TableSwitchInsnNode(min, max, clone(dflt, labels), clone( - this.labels, labels)); + this.labels, labels)).cloneAnnotations(this); } } \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/tree/TryCatchBlockNode.java b/src/java/nginx/clojure/asm/tree/TryCatchBlockNode.java index 9cd79ce1..508eba03 100644 --- a/src/java/nginx/clojure/asm/tree/TryCatchBlockNode.java +++ b/src/java/nginx/clojure/asm/tree/TryCatchBlockNode.java @@ -29,6 +29,8 @@ */ package nginx.clojure.asm.tree; +import java.util.List; + import nginx.clojure.asm.MethodVisitor; /** @@ -59,6 +61,26 @@ public class TryCatchBlockNode { */ public String type; + /** + * The runtime visible type annotations on the exception handler type. This + * list is a list of {@link TypeAnnotationNode} objects. May be + * null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations on the exception handler type. + * This list is a list of {@link TypeAnnotationNode} objects. May be + * null. + * + * @associates nginx.clojure.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * Constructs a new {@link TryCatchBlockNode}. * @@ -81,6 +103,29 @@ public TryCatchBlockNode(final LabelNode start, final LabelNode end, this.type = type; } + /** + * Updates the index of this try catch block in the method's list of try + * catch block nodes. This index maybe stored in the 'target' field of the + * type annotations of this block. + * + * @param index + * the new index of this try catch block in the method's list of + * try catch block nodes. + */ + public void updateIndex(final int index) { + int newTypeRef = 0x42000000 | (index << 8); + if (visibleTypeAnnotations != null) { + for (TypeAnnotationNode tan : visibleTypeAnnotations) { + tan.typeRef = newTypeRef; + } + } + if (invisibleTypeAnnotations != null) { + for (TypeAnnotationNode tan : invisibleTypeAnnotations) { + tan.typeRef = newTypeRef; + } + } + } + /** * Makes the given visitor visit this try catch block. * @@ -90,5 +135,19 @@ public TryCatchBlockNode(final LabelNode start, final LabelNode end, public void accept(final MethodVisitor mv) { mv.visitTryCatchBlock(start.getLabel(), end.getLabel(), handler == null ? null : handler.getLabel(), type); + int n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(mv.visitTryCatchAnnotation(an.typeRef, an.typePath, + an.desc, true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(mv.visitTryCatchAnnotation(an.typeRef, an.typePath, + an.desc, false)); + } } } diff --git a/src/java/nginx/clojure/asm/tree/TypeAnnotationNode.java b/src/java/nginx/clojure/asm/tree/TypeAnnotationNode.java new file mode 100644 index 00000000..ca59b4d5 --- /dev/null +++ b/src/java/nginx/clojure/asm/tree/TypeAnnotationNode.java @@ -0,0 +1,100 @@ +/*** + * 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.tree; + +import nginx.clojure.asm.Opcodes; +import nginx.clojure.asm.TypePath; +import nginx.clojure.asm.TypeReference; + +/** + * A node that represents a type annotationn. + * + * @author Eric Bruneton + */ +public class TypeAnnotationNode extends AnnotationNode { + + /** + * A reference to the annotated type. See {@link TypeReference}. + */ + public int typeRef; + + /** + * The path to the annotated type argument, wildcard bound, array element + * type, or static outer type within the referenced type. May be + * null if the annotation targets 'typeRef' as a whole. + */ + public TypePath typePath; + + /** + * Constructs a new {@link AnnotationNode}. Subclasses must not use this + * constructor. Instead, they must use the + * {@link #TypeAnnotationNode(int, int, TypePath, String)} version. + * + * @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 desc + * the class descriptor of the annotation class. + * @throws IllegalStateException + * If a subclass calls this constructor. + */ + public TypeAnnotationNode(final int typeRef, final TypePath typePath, + final String desc) { + this(Opcodes.ASM5, typeRef, typePath, desc); + if (getClass() != TypeAnnotationNode.class) { + throw new IllegalStateException(); + } + } + + /** + * Constructs a new {@link AnnotationNode}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @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 desc + * the class descriptor of the annotation class. + */ + public TypeAnnotationNode(final int api, final int typeRef, + final TypePath typePath, final String desc) { + super(api, desc); + this.typeRef = typeRef; + this.typePath = typePath; + } +} diff --git a/src/java/nginx/clojure/asm/tree/TypeInsnNode.java b/src/java/nginx/clojure/asm/tree/TypeInsnNode.java index 035d1ee1..178dc602 100644 --- a/src/java/nginx/clojure/asm/tree/TypeInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/TypeInsnNode.java @@ -81,10 +81,11 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { mv.visitTypeInsn(opcode, desc); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new TypeInsnNode(opcode, desc); + return new TypeInsnNode(opcode, desc).cloneAnnotations(this); } } \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/tree/VarInsnNode.java b/src/java/nginx/clojure/asm/tree/VarInsnNode.java index 22f6a04d..66a6ae55 100644 --- a/src/java/nginx/clojure/asm/tree/VarInsnNode.java +++ b/src/java/nginx/clojure/asm/tree/VarInsnNode.java @@ -84,10 +84,11 @@ public int getType() { @Override public void accept(final MethodVisitor mv) { mv.visitVarInsn(opcode, var); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new VarInsnNode(opcode, var); + return new VarInsnNode(opcode, var).cloneAnnotations(this); } } \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/tree/analysis/Analyzer.java b/src/java/nginx/clojure/asm/tree/analysis/Analyzer.java index 4d60163d..6cacdd5d 100644 --- a/src/java/nginx/clojure/asm/tree/analysis/Analyzer.java +++ b/src/java/nginx/clojure/asm/tree/analysis/Analyzer.java @@ -102,6 +102,7 @@ public Analyzer(final Interpreter interpreter) { * @throws AnalyzerException * if a problem occurs during the analysis. */ + @SuppressWarnings("unchecked") public Frame[] analyze(final String owner, final MethodNode m) throws AnalyzerException { if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) { diff --git a/src/java/nginx/clojure/asm/tree/analysis/AnalyzerException.java b/src/java/nginx/clojure/asm/tree/analysis/AnalyzerException.java index 7e3e3848..83209b56 100644 --- a/src/java/nginx/clojure/asm/tree/analysis/AnalyzerException.java +++ b/src/java/nginx/clojure/asm/tree/analysis/AnalyzerException.java @@ -37,6 +37,7 @@ * @author Bing Ran * @author Eric Bruneton */ +@SuppressWarnings("serial") public class AnalyzerException extends Exception { public final AbstractInsnNode node; diff --git a/src/java/nginx/clojure/asm/tree/analysis/BasicInterpreter.java b/src/java/nginx/clojure/asm/tree/analysis/BasicInterpreter.java index c40df7a8..adb653ac 100644 --- a/src/java/nginx/clojure/asm/tree/analysis/BasicInterpreter.java +++ b/src/java/nginx/clojure/asm/tree/analysis/BasicInterpreter.java @@ -53,7 +53,7 @@ public class BasicInterpreter extends Interpreter implements Opcodes { public BasicInterpreter() { - super(ASM4); + super(ASM5); } protected BasicInterpreter(final int api) { diff --git a/src/java/nginx/clojure/asm/tree/analysis/BasicVerifier.java b/src/java/nginx/clojure/asm/tree/analysis/BasicVerifier.java index c14fb5a8..aaecd54e 100644 --- a/src/java/nginx/clojure/asm/tree/analysis/BasicVerifier.java +++ b/src/java/nginx/clojure/asm/tree/analysis/BasicVerifier.java @@ -47,7 +47,7 @@ public class BasicVerifier extends BasicInterpreter { public BasicVerifier() { - super(ASM4); + super(ASM5); } protected BasicVerifier(final int api) { diff --git a/src/java/nginx/clojure/asm/tree/analysis/Frame.java b/src/java/nginx/clojure/asm/tree/analysis/Frame.java index b06b399f..a4629427 100644 --- a/src/java/nginx/clojure/asm/tree/analysis/Frame.java +++ b/src/java/nginx/clojure/asm/tree/analysis/Frame.java @@ -83,6 +83,7 @@ public class Frame { * @param nStack * the maximum stack size of the frame. */ + @SuppressWarnings("unchecked") public Frame(final int nLocals, final int nStack) { this.values = (V[]) new Value[nLocals + nStack]; this.locals = nLocals; @@ -133,6 +134,15 @@ public int getLocals() { return locals; } + /** + * Returns the maximum stack size of this frame. + * + * @return the maximum stack size of this frame. + */ + public int getMaxStackSize() { + return values.length - locals; + } + /** * Returns the value of the given local variable. * @@ -716,14 +726,14 @@ public boolean merge(final Frame frame, final boolean[] access) { */ @Override public String toString() { - StringBuffer b = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (int i = 0; i < getLocals(); ++i) { - b.append(getLocal(i)); + sb.append(getLocal(i)); } - b.append(' '); + sb.append(' '); for (int i = 0; i < getStackSize(); ++i) { - b.append(getStack(i).toString()); + sb.append(getStack(i).toString()); } - return b.toString(); + return sb.toString(); } } diff --git a/src/java/nginx/clojure/asm/tree/analysis/SimpleVerifier.java b/src/java/nginx/clojure/asm/tree/analysis/SimpleVerifier.java index 595b5fa2..eb050037 100644 --- a/src/java/nginx/clojure/asm/tree/analysis/SimpleVerifier.java +++ b/src/java/nginx/clojure/asm/tree/analysis/SimpleVerifier.java @@ -107,7 +107,7 @@ public SimpleVerifier(final Type currentClass, public SimpleVerifier(final Type currentClass, final Type currentSuperClass, final List currentClassInterfaces, final boolean isInterface) { - this(ASM4, currentClass, currentSuperClass, currentClassInterfaces, + this(ASM5, currentClass, currentSuperClass, currentClassInterfaces, isInterface); } diff --git a/src/java/nginx/clojure/asm/tree/analysis/SourceInterpreter.java b/src/java/nginx/clojure/asm/tree/analysis/SourceInterpreter.java index 68115a1d..7eec875a 100644 --- a/src/java/nginx/clojure/asm/tree/analysis/SourceInterpreter.java +++ b/src/java/nginx/clojure/asm/tree/analysis/SourceInterpreter.java @@ -50,7 +50,7 @@ public class SourceInterpreter extends Interpreter implements Opcodes { public SourceInterpreter() { - super(ASM4); + super(ASM5); } protected SourceInterpreter(final int api) { diff --git a/src/java/nginx/clojure/asm/tree/analysis/package.html b/src/java/nginx/clojure/asm/tree/analysis/package.html new file mode 100644 index 00000000..228da023 --- /dev/null +++ b/src/java/nginx/clojure/asm/tree/analysis/package.html @@ -0,0 +1,67 @@ + + + + +

      +Provides a framework for static code analysis based on the asm.tree package. +

      + +

      +Basic usage: +

      + +
      +ClassReader cr = new ClassReader(bytecode);
      +ClassNode cn = new ClassNode();
      +cr.accept(cn, ClassReader.SKIP_DEBUG);
      +
      +List methods = cn.methods;
      +for (int i = 0; i < methods.size(); ++i) {
      +    MethodNode method = (MethodNode) methods.get(i);
      +    if (method.instructions.size() > 0) {
      +        Analyzer a = new Analyzer(new BasicInterpreter());
      +        a.analyze(cn.name, method);
      +        Frame[] frames = a.getFrames();
      +        // Elements of the frames arrray now contains info for each instruction
      +        // from the analyzed method. BasicInterpreter creates BasicValue, that
      +        // is using simplified type system that distinguishes the UNINITIALZED,
      +        // INT, FLOAT, LONG, DOUBLE, REFERENCE and RETURNADDRESS types.
      +        ...
      +    }
      +}
      +
      + +

      +@since ASM 1.4.3 +

      + + + diff --git a/src/java/nginx/clojure/asm/tree/package.html b/src/java/nginx/clojure/asm/tree/package.html new file mode 100644 index 00000000..940b8767 --- /dev/null +++ b/src/java/nginx/clojure/asm/tree/package.html @@ -0,0 +1,192 @@ + + + + +

      +Provides an ASM visitor that constructs a tree representation of the +classes it visits. This class adapter can be useful to implement "complex" +class manipulation operations, i.e., operations that would be very hard to +implement without using a tree representation (such as optimizing the number +of local variables used by a method). +

      + +

      +However, this class adapter has a cost: it makes ASM bigger and slower. Indeed +it requires more than twenty new classes, and multiplies the time needed to +transform a class by almost two (it is almost two times faster to read, "modify" +and write a class with a ClassVisitor than with a ClassNode). This is why +this package is bundled in an optional asm-tree.jar library that +is separated from (but requires) the asm.jar library, which contains +the core ASM framework. This is also why it is recommended +not to use this class adapter when it is possible. +

      + +

      +The root class is the ClassNode, that can be created from existing bytecode. For example: +

      + +
      +  ClassReader cr = new ClassReader(source);
      +  ClassNode cn = new ClassNode();
      +  cr.accept(cn, true);
      +
      + +

      +Now the content of ClassNode can be modified and then +serialized back into bytecode: +

      + +
      +  ClassWriter cw = new ClassWriter(true);
      +  cn.accept(cw);
      +
      + +

      +Using a simple ClassVisitor it is possible to create MethodNode instances per-method. +In this example MethodNode is acting as a buffer that is flushed out at visitEnd() call: +

      + +
      +  ClassReader cr = new ClassReader(source);
      +  ClassWriter cw = new ClassWriter();
      +  ClassVisitor cv = new ClassVisitor(cw) {
      +    public MethodVisitor visitMethod(int access, String name,
      +        String desc, String signature, String[] exceptions) {
      +      final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
      +      MethodNode mn = new MethodNode(access, name, desc, signature, exceptions) {
      +        public void visitEnd() {
      +          // transform or analyze method code using tree API
      +          accept(mv);
      +        }
      +      };
      +    }
      +  };
      +  cr.accept(cv, true);
      +
      + +

      +Several strategies can be used to construct method code from scratch. The first +option is to create a MethodNode, and then create XxxInsnNode instances and +add them to the instructions list: +

      + +
      +MethodNode m = new MethodNode(...);
      +m.instructions.add(new VarInsnNode(ALOAD, 0));
      +...
      +
      + +

      +Alternatively, you can use the fact that MethodNode is a MethodVisitor, and use +that to create the XxxInsnNode and add them to the instructions list through +the standard MethodVisitor methods: +

      + +
      +MethodNode m = new MethodNode(...);
      +m.visitVarInsn(ALOAD, 0);
      +...
      +
      + +

      +If you cannot generate all the instructions in sequential order, i.e. if you +need to save some pointer in the instruction list and then insert instructions +at that place after other instructions have been generated, you can use InsnList +methods insert() and insertBefore() to insert instructions at a saved pointer. +

      + +
      +MethodNode m = new MethodNode(...);
      +m.visitVarInsn(ALOAD, 0);
      +AbstractInsnNode ptr = m.instructions.getLast();
      +m.visitVarInsn(ALOAD, 1);
      +// inserts an instruction between ALOAD 0 and ALOAD 1
      +m.instructions.insert(ptr, new VarInsnNode(ALOAD, 0));
      +...
      +
      + +

      +If you need to insert instructions while iterating over an existing instruction +list, you can also use several strategies. The first one is to use a +ListIterator over the instruction list: +

      + +
      +ListIterator it = m.instructions.iterator();
      +while (it.hasNext()) {
      +    AbstractInsnNode n = (AbstractInsnNode) it.next();
      +    if (...) {
      +        it.add(new VarInsnNode(ALOAD, 0));
      +    }
      +}
      +
      + +

      +It is also possible to convert an instruction list into an array and iterate trough +array elements: +

      + +
      +AbstractInsnNode[] insns = m.instructions.toArray();
      +for(int i = 0; i<insns.length; i++) {
      +    AbstractInsnNode n = insns[i];
      +    if (...) {
      +        m.instructions.insert(n, new VarInsnNode(ALOAD, 0));
      +    }
      +}
      +
      + +

      +If you want to insert these instructions through the MethodVisitor methods, +you can use another instance of MethodNode as a MethodVisitor and then +insert instructions collected by that instance into the instruction list. +For example: +

      + +
      +AbstractInsnNode[] insns = m.instructions.toArray();
      +for(int i = 0; i<insns.length; i++) {
      +    AbstractInsnNode n = insns[i];
      +    if (...) {
      +        MethodNode mn = new MethodNode();
      +        mn.visitVarInsn(ALOAD, 0);
      +        mn.visitVarInsn(ALOAD, 1);
      +        m.instructions.insert(n, mn.instructions);
      +    }
      +}
      +
      + +

      +@since ASM 1.3.3 +

      + + + diff --git a/src/java/nginx/clojure/asm/util/ASMifier.java b/src/java/nginx/clojure/asm/util/ASMifier.java index 6f0fbd94..9226c1d1 100644 --- a/src/java/nginx/clojure/asm/util/ASMifier.java +++ b/src/java/nginx/clojure/asm/util/ASMifier.java @@ -40,6 +40,7 @@ import nginx.clojure.asm.Label; import nginx.clojure.asm.Opcodes; import nginx.clojure.asm.Type; +import nginx.clojure.asm.TypePath; /** * A {@link Printer} that prints the ASM code to generate the classes if visits. @@ -83,9 +84,15 @@ public class ASMifier extends Printer { * Constructs a new {@link ASMifier}. Subclasses must not use this * constructor. Instead, they must use the * {@link #ASMifier(int, String, int)} version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public ASMifier() { - this(Opcodes.ASM4, "cw", 0); + this(Opcodes.ASM5, "cw", 0); + if (getClass() != ASMifier.class) { + throw new IllegalStateException(); + } } /** @@ -93,7 +100,7 @@ public ASMifier() { * * @param api * the ASM API version implemented by this class. Must be one of - * {@link Opcodes#ASM4}. + * {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param name * the name of the visitor variable in the produced code. * @param id @@ -170,7 +177,6 @@ public void visit(final int version, final int access, final String name, } text.add("import java.util.*;\n"); text.add("import nginx.clojure.asm.*;\n"); - text.add("import nginx.clojure.asm.attrs.*;\n"); text.add("public class " + simpleName + "Dump implements Opcodes {\n\n"); text.add("public static byte[] dump () throws Exception {\n\n"); text.add("ClassWriter cw = new ClassWriter(0);\n"); @@ -201,10 +207,7 @@ public void visit(final int version, final int access, final String name, break; case Opcodes.V1_7: buf.append("V1_7"); - break; - case Opcodes.V1_8: - buf.append("V1_7"); - break; + break; default: buf.append(version); break; @@ -263,6 +266,12 @@ public ASMifier visitClassAnnotation(final String desc, return visitAnnotation(desc, visible); } + @Override + public ASMifier visitClassTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitClassAttribute(final Attribute attr) { visitAttribute(attr); @@ -425,6 +434,12 @@ public ASMifier visitFieldAnnotation(final String desc, return visitAnnotation(desc, visible); } + @Override + public ASMifier visitFieldTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitFieldAttribute(final Attribute attr) { visitAttribute(attr); @@ -441,6 +456,16 @@ public void visitFieldEnd() { // Methods // ------------------------------------------------------------------------ + @Override + public void visitParameter(String parameterName, int access) { + buf.setLength(0); + buf.append(name).append(".visitParameter("); + appendString(buf, parameterName); + buf.append(", "); + appendAccess(access); + text.add(buf.append(");\n").toString()); + } + @Override public ASMifier visitAnnotationDefault() { buf.setLength(0); @@ -459,6 +484,12 @@ public ASMifier visitMethodAnnotation(final String desc, return visitAnnotation(desc, visible); } + @Override + public ASMifier visitMethodTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public ASMifier visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { @@ -585,9 +616,30 @@ public void visitFieldInsn(final int opcode, final String owner, text.add(buf.toString()); } + @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(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { buf.setLength(0); buf.append(this.name).append(".visitMethodInsn(") .append(OPCODES[opcode]).append(", "); @@ -596,6 +648,8 @@ public void visitMethodInsn(final int opcode, final String owner, appendConstant(name); buf.append(", "); appendConstant(desc); + buf.append(", "); + buf.append(itf ? "true" : "false"); buf.append(");\n"); text.add(buf.toString()); } @@ -713,6 +767,13 @@ public void visitMultiANewArrayInsn(final String desc, final int dims) { text.add(buf.toString()); } + @Override + public ASMifier visitInsnAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation("visitInsnAnnotation", typeRef, typePath, + desc, visible); + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -732,6 +793,13 @@ public void visitTryCatchBlock(final Label start, final Label end, text.add(buf.toString()); } + @Override + public ASMifier visitTryCatchAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation("visitTryCatchAnnotation", typeRef, + typePath, desc, visible); + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -751,6 +819,43 @@ public void visitLocalVariable(final String name, final String desc, text.add(buf.toString()); } + @Override + public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath, + Label[] start, Label[] end, int[] index, String desc, + boolean visible) { + buf.setLength(0); + buf.append("{\n").append("av0 = ").append(name) + .append(".visitLocalVariableAnnotation("); + buf.append(typeRef); + if (typePath == null) { + buf.append(", null, "); + } else { + buf.append(", TypePath.fromString(\"").append(typePath).append("\"), "); + } + buf.append("new Label[] {"); + for (int i = 0; i < start.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(start[i]); + } + buf.append(" }, new Label[] {"); + for (int i = 0; i < end.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(end[i]); + } + buf.append(" }, new int[] {"); + for (int i = 0; i < index.length; ++i) { + buf.append(i == 0 ? " " : ", ").append(index[i]); + } + buf.append(" }, "); + appendConstant(desc); + buf.append(", ").append(visible).append(");\n"); + text.add(buf.toString()); + ASMifier a = createASMifier("av", 0); + text.add(a.getText()); + text.add("}\n"); + return a; + } + @Override public void visitLineNumber(final int line, final Label start) { buf.setLength(0); @@ -792,6 +897,32 @@ public ASMifier visitAnnotation(final String desc, final boolean visible) { return a; } + public ASMifier visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation("visitTypeAnnotation", typeRef, typePath, + desc, visible); + } + + public ASMifier visitTypeAnnotation(final String method, final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + buf.setLength(0); + buf.append("{\n").append("av0 = ").append(name).append(".") + .append(method).append("("); + buf.append(typeRef); + if (typePath == null) { + buf.append(", null, "); + } else { + buf.append(", TypePath.fromString(\"").append(typePath).append("\"), "); + } + appendConstant(desc); + buf.append(", ").append(visible).append(");\n"); + text.add(buf.toString()); + ASMifier a = createASMifier("av", 0); + text.add(a.getText()); + text.add("}\n"); + return a; + } + public void visitAttribute(final Attribute attr) { buf.setLength(0); buf.append("// ATTRIBUTE ").append(attr.type).append('\n'); @@ -812,7 +943,7 @@ public void visitAttribute(final Attribute attr) { // ------------------------------------------------------------------------ protected ASMifier createASMifier(final String name, final int id) { - return new ASMifier(Opcodes.ASM4, name, id); + return new ASMifier(Opcodes.ASM5, name, id); } /** @@ -953,6 +1084,13 @@ void appendAccess(final int access) { buf.append("ACC_DEPRECATED"); first = false; } + if ((access & Opcodes.ACC_MANDATED) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_MANDATED"); + first = false; + } if (first) { buf.append('0'); } diff --git a/src/java/nginx/clojure/asm/util/CheckAnnotationAdapter.java b/src/java/nginx/clojure/asm/util/CheckAnnotationAdapter.java index f2242f7f..e48a732b 100644 --- a/src/java/nginx/clojure/asm/util/CheckAnnotationAdapter.java +++ b/src/java/nginx/clojure/asm/util/CheckAnnotationAdapter.java @@ -49,7 +49,7 @@ public CheckAnnotationAdapter(final AnnotationVisitor av) { } CheckAnnotationAdapter(final AnnotationVisitor av, final boolean named) { - super(Opcodes.ASM4, av); + super(Opcodes.ASM5, av); this.named = named; } @@ -70,7 +70,7 @@ public void visit(final String name, final Object value) { } if (value instanceof Type) { int sort = ((Type) value).getSort(); - if (sort != Type.OBJECT && sort != Type.ARRAY) { + if (sort == Type.METHOD) { throw new IllegalArgumentException("Invalid annotation value"); } } diff --git a/src/java/nginx/clojure/asm/util/CheckClassAdapter.java b/src/java/nginx/clojure/asm/util/CheckClassAdapter.java index 3f673650..9f7d5f23 100644 --- a/src/java/nginx/clojure/asm/util/CheckClassAdapter.java +++ b/src/java/nginx/clojure/asm/util/CheckClassAdapter.java @@ -46,6 +46,8 @@ import nginx.clojure.asm.MethodVisitor; import nginx.clojure.asm.Opcodes; import nginx.clojure.asm.Type; +import nginx.clojure.asm.TypePath; +import nginx.clojure.asm.TypeReference; import nginx.clojure.asm.tree.ClassNode; import nginx.clojure.asm.tree.MethodNode; import nginx.clojure.asm.tree.analysis.Analyzer; @@ -106,7 +108,7 @@ * 00071 LinkedBlockingQueue$Itr . I . . . . . . : * ILOAD 1 * 00072 ? - * INVOKESPECIAL java/lang/Integer. (I)V + * INVOKESPECIAL java/lang/Integer.<init> (I)V * ... * * @@ -215,7 +217,7 @@ public static void verify(final ClassReader cr, final ClassLoader loader, List interfaces = new ArrayList(); for (Iterator i = cn.interfaces.iterator(); i.hasNext();) { - interfaces.add(Type.getObjectType(i.next().toString())); + interfaces.add(Type.getObjectType(i.next())); } for (int i = 0; i < methods.size(); ++i) { @@ -267,26 +269,26 @@ static void printAnalyzerResult(MethodNode method, Analyzer a, for (int j = 0; j < method.instructions.size(); ++j) { method.instructions.get(j).accept(mv); - StringBuffer s = new StringBuffer(); + StringBuilder sb = new StringBuilder(); Frame f = frames[j]; if (f == null) { - s.append('?'); + sb.append('?'); } else { for (int k = 0; k < f.getLocals(); ++k) { - s.append(getShortName(f.getLocal(k).toString())) + sb.append(getShortName(f.getLocal(k).toString())) .append(' '); } - s.append(" : "); + sb.append(" : "); for (int k = 0; k < f.getStackSize(); ++k) { - s.append(getShortName(f.getStack(k).toString())) + sb.append(getShortName(f.getStack(k).toString())) .append(' '); } } - while (s.length() < method.maxStack + method.maxLocals + 1) { - s.append(' '); + while (sb.length() < method.maxStack + method.maxLocals + 1) { + sb.append(' '); } pw.print(Integer.toString(j + 100000).substring(1)); - pw.print(" " + s + " : " + t.text.get(t.text.size() - 1)); + pw.print(" " + sb + " : " + t.text.get(t.text.size() - 1)); } for (int j = 0; j < method.tryCatchBlocks.size(); ++j) { method.tryCatchBlocks.get(j).accept(mv); @@ -328,9 +330,14 @@ public CheckClassAdapter(final ClassVisitor cv) { * false to not perform any data flow check (see * {@link CheckMethodAdapter}). This option requires valid * maxLocals and maxStack values. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public CheckClassAdapter(final ClassVisitor cv, final boolean checkDataFlow) { - this(Opcodes.ASM4, cv, checkDataFlow); + this(Opcodes.ASM5, cv, checkDataFlow); + if (getClass() != CheckClassAdapter.class) { + throw new IllegalStateException(); + } } /** @@ -338,7 +345,7 @@ public CheckClassAdapter(final ClassVisitor cv, final boolean checkDataFlow) { * * @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 adapter must delegate calls. * @param checkDataFlow @@ -524,6 +531,23 @@ public AnnotationVisitor visitAnnotation(final String desc, return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); } + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkState(); + int sort = typeRef >>> 24; + if (sort != TypeReference.CLASS_TYPE_PARAMETER + && sort != TypeReference.CLASS_TYPE_PARAMETER_BOUND + && sort != TypeReference.CLASS_EXTENDS) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, + typePath, desc, visible)); + } + @Override public void visitAttribute(final Attribute attr) { checkState(); @@ -668,6 +692,77 @@ public static void checkFieldSignature(final String signature) { } } + /** + * Checks the reference to a type in a type annotation. + * + * @param typeRef + * a reference to an annotated type. + * @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. + */ + static void checkTypeRefAndPath(int typeRef, TypePath typePath) { + int mask = 0; + switch (typeRef >>> 24) { + case TypeReference.CLASS_TYPE_PARAMETER: + case TypeReference.METHOD_TYPE_PARAMETER: + case TypeReference.METHOD_FORMAL_PARAMETER: + mask = 0xFFFF0000; + break; + case TypeReference.FIELD: + case TypeReference.METHOD_RETURN: + case TypeReference.METHOD_RECEIVER: + case TypeReference.LOCAL_VARIABLE: + case TypeReference.RESOURCE_VARIABLE: + case TypeReference.INSTANCEOF: + case TypeReference.NEW: + case TypeReference.CONSTRUCTOR_REFERENCE: + case TypeReference.METHOD_REFERENCE: + mask = 0xFF000000; + break; + case TypeReference.CLASS_EXTENDS: + case TypeReference.CLASS_TYPE_PARAMETER_BOUND: + case TypeReference.METHOD_TYPE_PARAMETER_BOUND: + case TypeReference.THROWS: + case TypeReference.EXCEPTION_PARAMETER: + mask = 0xFFFFFF00; + break; + case TypeReference.CAST: + case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: + case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: + mask = 0xFF0000FF; + break; + default: + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(typeRef >>> 24)); + } + if ((typeRef & ~mask) != 0) { + throw new IllegalArgumentException("Invalid type reference 0x" + + Integer.toHexString(typeRef)); + } + if (typePath != null) { + for (int i = 0; i < typePath.getLength(); ++i) { + int step = typePath.getStep(i); + if (step != TypePath.ARRAY_ELEMENT + && step != TypePath.INNER_TYPE + && step != TypePath.TYPE_ARGUMENT + && step != TypePath.WILDCARD_BOUND) { + throw new IllegalArgumentException( + "Invalid type path step " + i + " in " + typePath); + } + if (step != TypePath.TYPE_ARGUMENT + && typePath.getStepArgument(i) != 0) { + throw new IllegalArgumentException( + "Invalid type path step argument for step " + i + + " in " + typePath); + } + } + } + } + /** * Checks the formal type parameters of a class or method signature. * diff --git a/src/java/nginx/clojure/asm/util/CheckFieldAdapter.java b/src/java/nginx/clojure/asm/util/CheckFieldAdapter.java index 5a294f32..2450dcea 100644 --- a/src/java/nginx/clojure/asm/util/CheckFieldAdapter.java +++ b/src/java/nginx/clojure/asm/util/CheckFieldAdapter.java @@ -33,6 +33,8 @@ import nginx.clojure.asm.Attribute; import nginx.clojure.asm.FieldVisitor; import nginx.clojure.asm.Opcodes; +import nginx.clojure.asm.TypePath; +import nginx.clojure.asm.TypeReference; /** * A {@link FieldVisitor} that checks that its methods are properly used. @@ -48,9 +50,14 @@ public class CheckFieldAdapter extends FieldVisitor { * * @param fv * the field visitor to which this adapter must delegate calls. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public CheckFieldAdapter(final FieldVisitor fv) { - this(Opcodes.ASM4, fv); + this(Opcodes.ASM5, fv); + if (getClass() != CheckFieldAdapter.class) { + throw new IllegalStateException(); + } } /** @@ -58,7 +65,7 @@ public CheckFieldAdapter(final FieldVisitor fv) { * * @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 adapter must delegate calls. */ @@ -74,6 +81,21 @@ public AnnotationVisitor visitAnnotation(final String desc, return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); } + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkEnd(); + int sort = typeRef >>> 24; + if (sort != TypeReference.FIELD) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, + typePath, desc, visible)); + } + @Override public void visitAttribute(final Attribute attr) { checkEnd(); diff --git a/src/java/nginx/clojure/asm/util/CheckMethodAdapter.java b/src/java/nginx/clojure/asm/util/CheckMethodAdapter.java index 1f6bdc9c..b31b1dbd 100644 --- a/src/java/nginx/clojure/asm/util/CheckMethodAdapter.java +++ b/src/java/nginx/clojure/asm/util/CheckMethodAdapter.java @@ -46,6 +46,8 @@ import nginx.clojure.asm.MethodVisitor; import nginx.clojure.asm.Opcodes; import nginx.clojure.asm.Type; +import nginx.clojure.asm.TypePath; +import nginx.clojure.asm.TypeReference; import nginx.clojure.asm.tree.MethodNode; import nginx.clojure.asm.tree.analysis.Analyzer; import nginx.clojure.asm.tree.analysis.BasicValue; @@ -390,10 +392,15 @@ public CheckMethodAdapter(final MethodVisitor mv) { * the method visitor to which this adapter must delegate calls. * @param labels * a map of already visited labels (in other methods). + * @throws IllegalStateException + * If a subclass calls this constructor. */ public CheckMethodAdapter(final MethodVisitor mv, final Map labels) { - this(Opcodes.ASM4, mv, labels); + this(Opcodes.ASM5, mv, labels); + if (getClass() != CheckMethodAdapter.class) { + throw new IllegalStateException(); + } } /** @@ -401,6 +408,9 @@ public CheckMethodAdapter(final MethodVisitor mv, * will not perform any data flow check (see * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). * + * @param api + * the ASM API version implemented by this CheckMethodAdapter. + * Must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param mv * the method visitor to which this adapter must delegate calls. * @param labels @@ -434,7 +444,7 @@ protected CheckMethodAdapter(final int api, final MethodVisitor mv, public CheckMethodAdapter(final int access, final String name, final String desc, final MethodVisitor cmv, final Map labels) { - this(new MethodNode(access, name, desc, null, null) { + this(new MethodNode(Opcodes.ASM5, access, name, desc, null, null) { @Override public void visitEnd() { Analyzer a = new Analyzer( @@ -461,6 +471,16 @@ public void visitEnd() { this.access = access; } + @Override + public void visitParameter(String name, int access) { + if (name != null) { + checkUnqualifiedName(version, name, "name"); + } + CheckClassAdapter.checkAccess(access, Opcodes.ACC_FINAL + + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC); + super.visitParameter(name, access); + } + @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { @@ -469,6 +489,26 @@ public AnnotationVisitor visitAnnotation(final String desc, return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); } + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkEndMethod(); + int sort = typeRef >>> 24; + if (sort != TypeReference.METHOD_TYPE_PARAMETER + && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND + && sort != TypeReference.METHOD_RETURN + && sort != TypeReference.METHOD_RECEIVER + && sort != TypeReference.METHOD_FORMAL_PARAMETER + && sort != TypeReference.THROWS) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, + typePath, desc, visible)); + } + @Override public AnnotationVisitor visitAnnotationDefault() { checkEndMethod(); @@ -647,9 +687,30 @@ public void visitFieldInsn(final int opcode, final String owner, ++insnCount; } + @Deprecated @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { + public void visitMethodInsn(int opcode, String owner, String name, + 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(int opcode, String owner, String name, + String desc, 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) { checkStartCode(); checkEndCode(); checkOpcode(opcode, 5); @@ -658,7 +719,27 @@ public void visitMethodInsn(final int opcode, final String owner, } checkInternalName(owner, "owner"); checkMethodDesc(desc); - super.visitMethodInsn(opcode, owner, name, desc); + if (opcode == Opcodes.INVOKEVIRTUAL && itf) { + throw new IllegalArgumentException( + "INVOKEVIRTUAL can't be used with interfaces"); + } + if (opcode == Opcodes.INVOKEINTERFACE && !itf) { + throw new IllegalArgumentException( + "INVOKEINTERFACE can't be used with classes"); + } + if (opcode == Opcodes.INVOKESPECIAL && itf + && (version & 0xFFFF) < Opcodes.V1_8) { + throw new IllegalArgumentException( + "INVOKESPECIAL can't be used with interfaces prior to Java 8"); + } + + // 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. + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } ++insnCount; } @@ -701,7 +782,7 @@ public void visitLabel(final Label label) { if (labels.get(label) != null) { throw new IllegalArgumentException("Already visited label"); } - labels.put(label, new Integer(insnCount)); + labels.put(label, insnCount); super.visitLabel(label); } @@ -796,6 +877,29 @@ public void visitMultiANewArrayInsn(final String desc, final int dims) { ++insnCount; } + @Override + public AnnotationVisitor visitInsnAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkStartCode(); + checkEndCode(); + int sort = typeRef >>> 24; + if (sort != TypeReference.INSTANCEOF && sort != TypeReference.NEW + && sort != TypeReference.CONSTRUCTOR_REFERENCE + && sort != TypeReference.METHOD_REFERENCE + && sort != TypeReference.CAST + && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT + && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitInsnAnnotation(typeRef, + typePath, desc, visible)); + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -820,6 +924,22 @@ public void visitTryCatchBlock(final Label start, final Label end, handlers.add(end); } + @Override + public AnnotationVisitor visitTryCatchAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkStartCode(); + checkEndCode(); + int sort = typeRef >>> 24; + if (sort != TypeReference.EXCEPTION_PARAMETER) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTryCatchAnnotation( + typeRef, typePath, desc, visible)); + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -840,6 +960,40 @@ public void visitLocalVariable(final String name, final String desc, super.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) { + checkStartCode(); + checkEndCode(); + int sort = typeRef >>> 24; + if (sort != TypeReference.LOCAL_VARIABLE + && sort != TypeReference.RESOURCE_VARIABLE) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + checkDesc(desc, false); + if (start == null || end == null || index == null + || end.length != start.length || index.length != start.length) { + throw new IllegalArgumentException( + "Invalid start, end and index arrays (must be non null and of identical length"); + } + for (int i = 0; i < start.length; ++i) { + checkLabel(start[i], true, "start label"); + checkLabel(end[i], true, "end label"); + checkUnsignedShort(index[i], "Invalid variable index"); + int s = labels.get(start[i]).intValue(); + int e = labels.get(end[i]).intValue(); + if (e < s) { + throw new IllegalArgumentException( + "Invalid start and end labels (end must be greater than start)"); + } + } + return super.visitLocalVariableAnnotation(typeRef, typePath, start, + end, index, desc, visible); + } + @Override public void visitLineNumber(final int line, final Label start) { checkStartCode(); @@ -1202,7 +1356,7 @@ static void checkInternalName(final String name, final int start, checkIdentifier(name, begin, slash, null); begin = slash + 1; } while (slash != max); - } catch (IllegalArgumentException _) { + } catch (IllegalArgumentException unused) { throw new IllegalArgumentException( "Invalid " + msg @@ -1280,7 +1434,7 @@ static int checkDesc(final String desc, final int start, } try { checkInternalName(desc, start + 1, index, null); - } catch (IllegalArgumentException _) { + } catch (IllegalArgumentException unused) { throw new IllegalArgumentException("Invalid descriptor: " + desc); } diff --git a/src/java/nginx/clojure/asm/util/CheckSignatureAdapter.java b/src/java/nginx/clojure/asm/util/CheckSignatureAdapter.java index 241a926a..980e0a9f 100644 --- a/src/java/nginx/clojure/asm/util/CheckSignatureAdapter.java +++ b/src/java/nginx/clojure/asm/util/CheckSignatureAdapter.java @@ -113,7 +113,7 @@ public class CheckSignatureAdapter extends SignatureVisitor { * null. */ public CheckSignatureAdapter(final int type, final SignatureVisitor sv) { - this(Opcodes.ASM4, type, sv); + this(Opcodes.ASM5, type, sv); } /** @@ -121,7 +121,7 @@ public CheckSignatureAdapter(final int type, final SignatureVisitor sv) { * * @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 type * the type of signature to be checked. See * {@link #CLASS_SIGNATURE}, {@link #METHOD_SIGNATURE} and diff --git a/src/java/nginx/clojure/asm/util/Printer.java b/src/java/nginx/clojure/asm/util/Printer.java index 38202014..c16f9142 100644 --- a/src/java/nginx/clojure/asm/util/Printer.java +++ b/src/java/nginx/clojure/asm/util/Printer.java @@ -37,6 +37,7 @@ import nginx.clojure.asm.Handle; import nginx.clojure.asm.Label; import nginx.clojure.asm.Opcodes; +import nginx.clojure.asm.TypePath; /** * An abstract converter from visit events to text. @@ -116,7 +117,7 @@ public abstract class Printer { /** * The ASM API version implemented by this class. 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; @@ -141,6 +142,10 @@ public abstract class Printer { /** * Constructs a new {@link Printer}. + * + * @param api + * the ASM API version implemented by this printer. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected Printer(final int api) { this.api = api; @@ -149,52 +154,189 @@ protected Printer(final int api) { } /** - * Class header. See {@link nginx.clojure.asm.ClassVisitor#visit}. + * Class header. + * See {@link nginx.clojure.asm.ClassVisitor#visit}. + * + * @param version + * the class version. + * @param access + * the class's access flags (see {@link Opcodes}). This parameter + * also indicates if the class is deprecated. + * @param name + * the internal name of the class (see + * {@link nginx.clojure.asm.Type#getInternalName() getInternalName}). + * @param signature + * the signature of this class. May be null if the class + * is not a generic one, and does not extend or implement generic + * classes or interfaces. + * @param superName + * the internal of name of the super class (see + * {@link nginx.clojure.asm.Type#getInternalName() getInternalName}). + * For interfaces, the super class is {@link Object}. May be + * null, but only for the {@link Object} class. + * @param interfaces + * the internal names of the class's interfaces (see + * {@link nginx.clojure.asm.Type#getInternalName() getInternalName}). + * May be null. */ public abstract void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces); /** - * Class source. See {@link nginx.clojure.asm.ClassVisitor#visitSource}. + * Class source. + * See {@link nginx.clojure.asm.ClassVisitor#visitSource}. + * + * @param source + * the name of the source file from which the class was compiled. + * May be null. + * @param debug + * additional debug information to compute the correspondance + * between source and compiled elements of the class. May be + * null. */ - public abstract void visitSource(final String file, final String debug); + public abstract void visitSource(final String source, final String debug); /** - * Class outer class. See - * {@link nginx.clojure.asm.ClassVisitor#visitOuterClass}. + * Class outer class. + * See {@link nginx.clojure.asm.ClassVisitor#visitOuterClass}. + * + * Visits the enclosing class of the class. This method must be called only + * if the class has an enclosing class. + * + * @param owner + * internal name of the enclosing class of the class. + * @param name + * the name of the method that contains the class, or + * null if the class is not enclosed in a method of its + * enclosing class. + * @param desc + * the descriptor of the method that contains the class, or + * null if the class is not enclosed in a method of its + * enclosing class. */ public abstract void visitOuterClass(final String owner, final String name, final String desc); /** - * Class annotation. See - * {@link nginx.clojure.asm.ClassVisitor#visitAnnotation}. + * Class annotation. + * See {@link nginx.clojure.asm.ClassVisitor#visitAnnotation}. + * + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return the printer */ public abstract Printer visitClassAnnotation(final String desc, final boolean visible); /** - * Class attribute. See - * {@link nginx.clojure.asm.ClassVisitor#visitAttribute}. + * Class type annotation. + * See {@link nginx.clojure.asm.ClassVisitor#visitTypeAnnotation}. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be + * {@link nginx.clojure.asm.TypeReference#CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, + * {@link nginx.clojure.asm.TypeReference#CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} + * or {@link nginx.clojure.asm.TypeReference#CLASS_EXTENDS CLASS_EXTENDS}. + * See {@link nginx.clojure.asm.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 the printer + */ + public Printer visitClassTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** + * Class attribute. + * See {@link nginx.clojure.asm.ClassVisitor#visitAttribute}. + * + * @param attr + * an attribute. */ public abstract void visitClassAttribute(final Attribute attr); /** - * Class inner name. See - * {@link nginx.clojure.asm.ClassVisitor#visitInnerClass}. + * Class inner name. + * See {@link nginx.clojure.asm.ClassVisitor#visitInnerClass}. + * + * @param name + * the internal name of an inner class (see + * {@link nginx.clojure.asm.Type#getInternalName() getInternalName}). + * @param outerName + * the internal name of the class to which the inner class + * belongs (see {@link nginx.clojure.asm.Type#getInternalName() getInternalName}). + * May be null for not member classes. + * @param innerName + * the (simple) name of the inner class inside its enclosing + * class. May be null for anonymous inner classes. + * @param access + * the access flags of the inner class as originally declared in + * the enclosing class. */ public abstract void visitInnerClass(final String name, final String outerName, final String innerName, final int access); /** - * Class field. See {@link nginx.clojure.asm.ClassVisitor#visitField}. + * Class field. + * See {@link nginx.clojure.asm.ClassVisitor#visitField}. + * + * @param access + * the field's access flags (see {@link Opcodes}). This parameter + * also indicates if the field is synthetic and/or deprecated. + * @param name + * the field's name. + * @param desc + * the field's descriptor (see {@link nginx.clojure.asm.Type Type}). + * @param signature + * the field's signature. May be null if the field's + * type does not use generic types. + * @param value + * the field's initial value. This parameter, which may be + * null if the field does not have an initial value, + * must be an {@link Integer}, a {@link Float}, a {@link Long}, a + * {@link Double} or a {@link String} (for int, + * float, long or String fields + * respectively). This parameter is only used for static + * fields. Its value is ignored for non static fields, which + * must be initialized through bytecode instructions in + * constructors or methods. + * @return the printer */ public abstract Printer visitField(final int access, final String name, final String desc, final String signature, final Object value); /** - * Class method. See {@link nginx.clojure.asm.ClassVisitor#visitMethod}. + * Class method. + * See {@link nginx.clojure.asm.ClassVisitor#visitMethod}. + * + * @param access + * the method's access flags (see {@link Opcodes}). This + * parameter also indicates if the method is synthetic and/or + * deprecated. + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link nginx.clojure.asm.Type Type}). + * @param signature + * the method's signature. May be null if the method + * parameters, return type and exceptions do not use generic + * types. + * @param exceptions + * the internal names of the method's exception classes (see + * {@link nginx.clojure.asm.Type#getInternalName() getInternalName}). May be + * null. + * @return the printer */ public abstract Printer visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions); @@ -209,26 +351,64 @@ public abstract Printer visitMethod(final int access, final String name, // ------------------------------------------------------------------------ /** - * Annotation value. See {@link nginx.clojure.asm.AnnotationVisitor#visit}. + * Annotation value. + * See {@link nginx.clojure.asm.AnnotationVisitor#visit}. + * + * @param name + * the value name. + * @param value + * the actual value, whose type must be {@link Byte}, + * {@link Boolean}, {@link Character}, {@link Short}, + * {@link Integer} , {@link Long}, {@link Float}, {@link Double}, + * {@link String} or {@link nginx.clojure.asm.Type} + * or OBJECT or ARRAY sort. + * This value can also be an array of byte, boolean, short, char, int, + * long, float or double values (this is equivalent to using + * {@link #visitArray visitArray} and visiting each array element + * in turn, but is more convenient). */ public abstract void visit(final String name, final Object value); /** - * Annotation enum value. See - * {@link nginx.clojure.asm.AnnotationVisitor#visitEnum}. + * Annotation enum value. + * See {@link nginx.clojure.asm.AnnotationVisitor#visitEnum}. + * + * Visits an enumeration value of the annotation. + * + * @param name + * the value name. + * @param desc + * the class descriptor of the enumeration class. + * @param value + * the actual enumeration value. */ public abstract void visitEnum(final String name, final String desc, final String value); /** - * Nested annotation value. See - * {@link nginx.clojure.asm.AnnotationVisitor#visitAnnotation}. + * Nested annotation value. + * See {@link nginx.clojure.asm.AnnotationVisitor#visitAnnotation}. + * + * @param name + * the value name. + * @param desc + * the class descriptor of the nested annotation class. + * @return the printer */ public abstract Printer visitAnnotation(final String name, final String desc); /** - * Annotation array value. See - * {@link nginx.clojure.asm.AnnotationVisitor#visitArray}. + * Annotation array value. + * See {@link nginx.clojure.asm.AnnotationVisitor#visitArray}. + * + * Visits an array value of the annotation. Note that arrays of primitive + * types (such as byte, boolean, short, char, int, long, float or double) + * can be passed as value to {@link #visit visit}. This is what + * {@link nginx.clojure.asm.ClassReader} does. + * + * @param name + * the value name. + * @return the printer */ public abstract Printer visitArray(final String name); @@ -242,20 +422,53 @@ public abstract void visitEnum(final String name, final String desc, // ------------------------------------------------------------------------ /** - * Field annotation. See - * {@link nginx.clojure.asm.FieldVisitor#visitAnnotation}. + * Field annotation. + * See {@link nginx.clojure.asm.FieldVisitor#visitAnnotation}. + * + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return the printer */ public abstract Printer visitFieldAnnotation(final String desc, final boolean visible); /** - * Field attribute. See - * {@link nginx.clojure.asm.FieldVisitor#visitAttribute}. + * Field type annotation. + * See {@link nginx.clojure.asm.FieldVisitor#visitTypeAnnotation}. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link nginx.clojure.asm.TypeReference#FIELD FIELD}. + * See {@link nginx.clojure.asm.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 the printer + */ + public Printer visitFieldTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** + * Field attribute. + * See {@link nginx.clojure.asm.FieldVisitor#visitAttribute}. + * + * @param attr + * an attribute. */ public abstract void visitFieldAttribute(final Attribute attr); /** - * Field end. See {@link nginx.clojure.asm.FieldVisitor#visitEnd}. + * Field end. + * See {@link nginx.clojure.asm.FieldVisitor#visitEnd}. */ public abstract void visitFieldEnd(); @@ -264,161 +477,647 @@ public abstract Printer visitFieldAnnotation(final String desc, // ------------------------------------------------------------------------ /** - * Method default annotation. See - * {@link nginx.clojure.asm.MethodVisitor#visitAnnotationDefault}. + * Method parameter. + * See {@link nginx.clojure.asm.MethodVisitor#visitParameter(String, int)}. + * + * @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) { + throw new RuntimeException("Must be overriden"); + } + + /** + * Method default annotation. + * See {@link nginx.clojure.asm.MethodVisitor#visitAnnotationDefault}. + * + * @return the printer */ public abstract Printer visitAnnotationDefault(); /** - * Method annotation. See - * {@link nginx.clojure.asm.MethodVisitor#visitAnnotation}. + * Method annotation. + * See {@link nginx.clojure.asm.MethodVisitor#visitAnnotation}. + * + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return the printer */ public abstract Printer visitMethodAnnotation(final String desc, final boolean visible); /** - * Method parameter annotation. See - * {@link nginx.clojure.asm.MethodVisitor#visitParameterAnnotation}. + * Method type annotation. + * See {@link nginx.clojure.asm.MethodVisitor#visitTypeAnnotation}. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link nginx.clojure.asm.TypeReference#FIELD FIELD}. + * See {@link nginx.clojure.asm.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 the printer + */ + public Printer visitMethodTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** + * Method parameter annotation. + * See {@link nginx.clojure.asm.MethodVisitor#visitParameterAnnotation}. + * + * @param parameter + * the parameter index. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return the printer */ public abstract Printer visitParameterAnnotation(final int parameter, final String desc, final boolean visible); /** - * Method attribute. See - * {@link nginx.clojure.asm.MethodVisitor#visitAttribute}. + * Method attribute. + * See {@link nginx.clojure.asm.MethodVisitor#visitAttribute}. + * + * @param attr + * an attribute. */ public abstract void visitMethodAttribute(final Attribute attr); /** - * Method start. See {@link nginx.clojure.asm.MethodVisitor#visitCode}. + * Method start. + * See {@link nginx.clojure.asm.MethodVisitor#visitCode}. */ public abstract void visitCode(); /** - * Method stack frame. See - * {@link nginx.clojure.asm.MethodVisitor#visitFrame}. + * Method stack frame. + * See {@link nginx.clojure.asm.MethodVisitor#visitFrame}. + * + * Visits the current state of the local variables and operand stack + * elements. This method must(*) be called just before any + * instruction i that follows an unconditional branch instruction + * such as GOTO or THROW, that is the target of a jump instruction, or that + * starts an exception handler block. The visited types must describe the + * values of the local variables and of the operand stack elements just + * before i is executed.
      + *
      + * (*) this is mandatory only for classes whose version is greater than or + * equal to {@link Opcodes#V1_6 V1_6}.
      + *
      + * The frames of a method must be given either in expanded form, or in + * compressed form (all frames must use the same format, i.e. you must not + * mix expanded and compressed frames within a single method): + *
        + *
      • In expanded form, all frames must have the F_NEW type.
      • + *
      • In compressed form, frames are basically "deltas" from the state of + * the previous frame: + *
          + *
        • {@link Opcodes#F_SAME} representing frame with exactly the same + * locals as the previous frame and with the empty stack.
        • + *
        • {@link Opcodes#F_SAME1} representing frame with exactly the same + * locals as the previous frame and with single value on the stack ( + * nStack is 1 and stack[0] contains value for the + * type of the stack item).
        • + *
        • {@link Opcodes#F_APPEND} representing frame with current locals are + * the same as the locals in the previous frame, except that additional + * locals are defined (nLocal is 1, 2 or 3 and + * local elements contains values representing added types).
        • + *
        • {@link Opcodes#F_CHOP} representing frame with current locals are the + * same as the locals in the previous frame, except that the last 1-3 locals + * are absent and with the empty stack (nLocals is 1, 2 or 3).
        • + *
        • {@link Opcodes#F_FULL} representing complete frame data.
        • + *
        + *
      • + *
      + *
      + * In both cases the first frame, corresponding to the method's parameters + * and access flags, is implicit and must not be visited. Also, it is + * illegal to visit two or more frames for the same code location (i.e., at + * least one instruction must be visited between two calls to visitFrame). + * + * @param type + * the type of this stack map frame. Must be + * {@link Opcodes#F_NEW} for expanded frames, or + * {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, + * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or + * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for + * compressed frames. + * @param nLocal + * the number of local variables in the visited frame. + * @param local + * the local variable types in this frame. This array must not be + * modified. Primitive types are represented by + * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, + * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are + * represented by a single element). Reference types are + * represented by String objects (representing internal names), + * and uninitialized types by Label objects (this label + * designates the NEW instruction that created this uninitialized + * value). + * @param nStack + * the number of operand stack elements in the visited frame. + * @param stack + * the operand stack types in this frame. This array must not be + * modified. Its content has the same format as the "local" + * array. + * @throws IllegalStateException + * if a frame is visited just after another one, without any + * instruction between the two (unless this frame is a + * Opcodes#F_SAME frame, in which case it is silently ignored). */ public abstract void visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack); /** - * Method instruction. See {@link nginx.clojure.asm.MethodVisitor#visitInsn} - * . + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitInsn} + * + * @param opcode + * the opcode of the instruction to be visited. This opcode is + * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, + * ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, + * FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, + * LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, + * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, + * SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, + * DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, + * IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, + * FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, + * IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, + * L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, + * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, + * DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, + * or MONITOREXIT. */ public abstract void visitInsn(final int opcode); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitIntInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitIntInsn}. + * + * @param opcode + * the opcode of the instruction to be visited. This opcode is + * either BIPUSH, SIPUSH or NEWARRAY. + * @param operand + * the operand of the instruction to be visited.
      + * When opcode is BIPUSH, operand value should be between + * Byte.MIN_VALUE and Byte.MAX_VALUE.
      + * When opcode is SIPUSH, operand value should be between + * Short.MIN_VALUE and Short.MAX_VALUE.
      + * When opcode is NEWARRAY, operand value should be one of + * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR}, + * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, + * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT}, + * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. */ public abstract void visitIntInsn(final int opcode, final int operand); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitVarInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitVarInsn}. + * + * @param opcode + * the opcode of the local variable instruction to be visited. + * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, + * ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var + * the operand of the instruction to be visited. This operand is + * the index of a local variable. */ public abstract void visitVarInsn(final int opcode, final int var); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitTypeInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitTypeInsn}. + * + /** + * Visits a type instruction. A type instruction is an instruction that + * takes the internal name of a class as parameter. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param type + * the operand of the instruction to be visited. This operand + * must be the internal name of an object or array class (see + * {@link nginx.clojure.asm.Type#getInternalName() getInternalName}). */ public abstract void visitTypeInsn(final int opcode, final String type); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitFieldInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitFieldInsn}. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner + * the internal name of the field's owner class (see + * {@link nginx.clojure.asm.Type#getInternalName() getInternalName}). + * @param name + * the field's name. + * @param desc + * the field's descriptor (see {@link nginx.clojure.asm.Type Type}). */ public abstract void visitFieldInsn(final int opcode, final String owner, final String name, final String desc); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitMethodInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitMethodInsn}. + * + * @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 nginx.clojure.asm.Type#getInternalName() getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link nginx.clojure.asm.Type Type}). */ - public abstract void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc); + @Deprecated + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc) { + if (api >= Opcodes.ASM5) { + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + throw new RuntimeException("Must be overriden"); + } + + /** + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitMethodInsn}. + * + * @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 nginx.clojure.asm.Type#getInternalName() getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link nginx.clojure.asm.Type Type}). + * @param itf + * if the method's owner class is an interface. + */ + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final 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; + } + throw new RuntimeException("Must be overriden"); + } /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitInvokeDynamicInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitInvokeDynamicInsn}. + * + * Visits an invokedynamic instruction. + * + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link nginx.clojure.asm.Type Type}). + * @param bsm + * the bootstrap method. + * @param bsmArgs + * the bootstrap method constant arguments. Each argument must be + * an {@link Integer}, {@link Float}, {@link Long}, + * {@link Double}, {@link String}, {@link nginx.clojure.asm.Type} or {@link Handle} + * value. This method is allowed to modify the content of the + * array so a caller should expect that this array may change. */ public abstract void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitJumpInsn}. + * Method jump instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitJumpInsn}. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, + * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, + * IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. + * @param label + * the operand of the instruction to be visited. This operand is + * a label that designates the instruction to which the jump + * instruction may jump. */ public abstract void visitJumpInsn(final int opcode, final Label label); /** - * Method label. See {@link nginx.clojure.asm.MethodVisitor#visitLabel}. + * Method label. + * See {@link nginx.clojure.asm.MethodVisitor#visitLabel}. + * + * @param label + * a {@link Label Label} object. */ public abstract void visitLabel(final Label label); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitLdcInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitLdcInsn}. + * + * Visits a LDC instruction. Note that new constant types may be added in + * future versions of the Java Virtual Machine. To easily detect new + * constant types, implementations of this method should check for + * unexpected constant types, like this: + * + *
      +     * if (cst instanceof Integer) {
      +     *     // ...
      +     * } else if (cst instanceof Float) {
      +     *     // ...
      +     * } else if (cst instanceof Long) {
      +     *     // ...
      +     * } else if (cst instanceof Double) {
      +     *     // ...
      +     * } else if (cst instanceof String) {
      +     *     // ...
      +     * } else if (cst instanceof Type) {
      +     *     int sort = ((Type) cst).getSort();
      +     *     if (sort == Type.OBJECT) {
      +     *         // ...
      +     *     } else if (sort == Type.ARRAY) {
      +     *         // ...
      +     *     } else if (sort == Type.METHOD) {
      +     *         // ...
      +     *     } else {
      +     *         // throw an exception
      +     *     }
      +     * } else if (cst instanceof Handle) {
      +     *     // ...
      +     * } else {
      +     *     // throw an exception
      +     * }
      +     * 
      + * + * @param cst + * the constant to be loaded on the stack. This parameter must be + * a non null {@link Integer}, a {@link Float}, a {@link Long}, a + * {@link Double}, a {@link String}, a {@link nginx.clojure.asm.Type} + * of OBJECT or ARRAY sort for .class constants, for classes whose + * version is 49.0, a {@link nginx.clojure.asm.Type} of METHOD sort or a + * {@link Handle} for MethodType and MethodHandle constants, for + * classes whose version is 51.0. */ public abstract void visitLdcInsn(final Object cst); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitIincInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitIincInsn}. + * + * @param var + * index of the local variable to be incremented. + * @param increment + * amount to increment the local variable by. */ public abstract void visitIincInsn(final int var, final int increment); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitTableSwitchInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitTableSwitchInsn}. + * + * @param min + * the minimum key value. + * @param max + * the maximum key value. + * @param dflt + * beginning of the default handler block. + * @param labels + * beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the min + i key. */ public abstract void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitLookupSwitchInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitLookupSwitchInsn}. + * + * @param dflt + * beginning of the default handler block. + * @param keys + * the values of the keys. + * @param labels + * beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the keys[i] key. */ public abstract void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels); /** - * Method instruction. See - * {@link nginx.clojure.asm.MethodVisitor#visitMultiANewArrayInsn}. + * Method instruction. + * See {@link nginx.clojure.asm.MethodVisitor#visitMultiANewArrayInsn}. + * + * @param desc + * an array type descriptor (see {@link nginx.clojure.asm.Type Type}). + * @param dims + * number of dimensions of the array to allocate. */ public abstract void visitMultiANewArrayInsn(final String desc, final int dims); /** - * Method exception handler. See - * {@link nginx.clojure.asm.MethodVisitor#visitTryCatchBlock}. + * Instruction type annotation. + * See {@link nginx.clojure.asm.MethodVisitor#visitInsnAnnotation}. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link nginx.clojure.asm.TypeReference#INSTANCEOF INSTANCEOF}, + * {@link nginx.clojure.asm.TypeReference#NEW NEW}, + * {@link nginx.clojure.asm.TypeReference#CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, + * {@link nginx.clojure.asm.TypeReference#METHOD_REFERENCE METHOD_REFERENCE}, + * {@link nginx.clojure.asm.TypeReference#CAST CAST}, + * {@link nginx.clojure.asm.TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link nginx.clojure.asm.TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link nginx.clojure.asm.TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, + * or {@link nginx.clojure.asm.TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT METHOD_REFERENCE_TYPE_ARGUMENT}. + * See {@link nginx.clojure.asm.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 the printer + */ + public Printer visitInsnAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** + * Method exception handler. + * See {@link nginx.clojure.asm.MethodVisitor#visitTryCatchBlock}. + * + * @param start + * beginning of the exception handler's scope (inclusive). + * @param end + * end of the exception handler's scope (exclusive). + * @param handler + * beginning of the exception handler's code. + * @param type + * internal name of the type of exceptions handled by the + * handler, or null to catch any exceptions (for + * "finally" blocks). + * @throws IllegalArgumentException + * if one of the labels has already been visited by this visitor + * (by the {@link #visitLabel visitLabel} method). */ public abstract void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type); /** - * Method debug info. See - * {@link nginx.clojure.asm.MethodVisitor#visitLocalVariable}. + * Try catch block type annotation. + * See {@link nginx.clojure.asm.MethodVisitor#visitTryCatchAnnotation}. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link nginx.clojure.asm.TypeReference#EXCEPTION_PARAMETER + * EXCEPTION_PARAMETER}. + * See {@link nginx.clojure.asm.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 the printer + */ + public Printer visitTryCatchAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** + * Method debug info. + * See {@link nginx.clojure.asm.MethodVisitor#visitLocalVariable}. + * + * @param name + * the name of a local variable. + * @param desc + * the type descriptor of this local variable. + * @param signature + * the type signature of this local variable. May be + * null if the local variable type does not use generic + * types. + * @param start + * the first instruction corresponding to the scope of this local + * variable (inclusive). + * @param end + * the last instruction corresponding to the scope of this local + * variable (exclusive). + * @param index + * the local variable's index. + * @throws IllegalArgumentException + * if one of the labels has not already been visited by this + * visitor (by the {@link #visitLabel visitLabel} method). */ public abstract void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index); /** - * Method debug info. See - * {@link nginx.clojure.asm.MethodVisitor#visitLineNumber}. + * Local variable type annotation. + * See {@link nginx.clojure.asm.MethodVisitor#visitTryCatchAnnotation}. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link nginx.clojure.asm.TypeReference#LOCAL_VARIABLE + * LOCAL_VARIABLE} or {@link nginx.clojure.asm.TypeReference#RESOURCE_VARIABLE + * RESOURCE_VARIABLE}. + * See {@link nginx.clojure.asm.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 the printer + */ + public Printer visitLocalVariableAnnotation(final int typeRef, + final TypePath typePath, final Label[] start, final Label[] end, + final int[] index, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** + * Method debug info. + * See {@link nginx.clojure.asm.MethodVisitor#visitLineNumber}. + * + * @param line + * a line number. This number refers to the source file from + * which the class was compiled. + * @param start + * the first instruction corresponding to this line number. + * @throws IllegalArgumentException + * if start has not already been visited by this + * visitor (by the {@link #visitLabel visitLabel} method). */ public abstract void visitLineNumber(final int line, final Label start); /** - * Method max stack and max locals. See - * {@link nginx.clojure.asm.MethodVisitor#visitMaxs}. + * Method max stack and max locals. + * See {@link nginx.clojure.asm.MethodVisitor#visitMaxs}. + * + * @param maxStack + * maximum stack size of the method. + * @param maxLocals + * maximum number of local variables for the method. */ public abstract void visitMaxs(final int maxStack, final int maxLocals); /** - * Method end. See {@link nginx.clojure.asm.MethodVisitor#visitEnd}. + * Method end. + * See {@link nginx.clojure.asm.MethodVisitor#visitEnd}. */ public abstract void visitMethodEnd(); @@ -497,4 +1196,4 @@ static void printList(final PrintWriter pw, final List l) { } } } -} +} \ No newline at end of file diff --git a/src/java/nginx/clojure/asm/util/Textifier.java b/src/java/nginx/clojure/asm/util/Textifier.java index 44743a99..0e6be8d5 100644 --- a/src/java/nginx/clojure/asm/util/Textifier.java +++ b/src/java/nginx/clojure/asm/util/Textifier.java @@ -40,11 +40,13 @@ import nginx.clojure.asm.Label; import nginx.clojure.asm.Opcodes; import nginx.clojure.asm.Type; +import nginx.clojure.asm.TypePath; +import nginx.clojure.asm.TypeReference; import nginx.clojure.asm.signature.SignatureReader; /** * A {@link Printer} that prints a disassembled view of the classes it visits. - * + * * @author Eric Bruneton */ public class Textifier extends Printer { @@ -135,23 +137,34 @@ public class Textifier extends Printer { */ protected Map labelNames; + /** + * Class access flags + */ + private int access; + private int valueNumber = 0; /** * Constructs a new {@link Textifier}. Subclasses must not use this * constructor. Instead, they must use the {@link #Textifier(int)} * version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public Textifier() { - this(Opcodes.ASM4); + this(Opcodes.ASM5); + if (getClass() != Textifier.class) { + throw new IllegalStateException(); + } } /** * Constructs a new {@link Textifier}. - * + * * @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}. */ protected Textifier(final int api) { super(api); @@ -161,10 +174,10 @@ protected Textifier(final int api) { * Prints a disassembled view of the given class to the standard output. *

      * Usage: Textifier [-debug] <binary class name or class file name > - * + * * @param args * the command line arguments. - * + * * @throws Exception * if the class cannot be found, or if an IO exception occurs. */ @@ -208,6 +221,7 @@ public static void main(final String[] args) throws Exception { public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { + this.access = access; int major = version & 0xFFFF; int minor = version >>> 16; buf.setLength(0); @@ -293,6 +307,13 @@ public Textifier visitClassAnnotation(final String desc, return visitAnnotation(desc, visible); } + @Override + public Printer visitClassTypeAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + text.add("\n"); + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitClassAttribute(final Attribute attr) { text.add("\n"); @@ -393,7 +414,7 @@ public Textifier visitMethod(final int access, final String name, } buf.append(tab); - appendAccess(access); + appendAccess(access & ~Opcodes.ACC_VOLATILE); if ((access & Opcodes.ACC_NATIVE) != 0) { buf.append("native "); } @@ -403,6 +424,11 @@ public Textifier visitMethod(final int access, final String name, if ((access & Opcodes.ACC_BRIDGE) != 0) { buf.append("bridge "); } + if ((this.access & Opcodes.ACC_INTERFACE) != 0 + && (access & Opcodes.ACC_ABSTRACT) == 0 + && (access & Opcodes.ACC_STATIC) == 0) { + buf.append("default "); + } buf.append(name); appendDescriptor(METHOD_DESCRIPTOR, desc); @@ -616,6 +642,12 @@ public Textifier visitFieldAnnotation(final String desc, return visitAnnotation(desc, visible); } + @Override + public Printer visitFieldTypeAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitFieldAttribute(final Attribute attr) { visitAttribute(attr); @@ -629,6 +661,16 @@ public void visitFieldEnd() { // Methods // ------------------------------------------------------------------------ + @Override + public void visitParameter(final String name, final int access) { + buf.setLength(0); + buf.append(tab2).append("// parameter "); + appendAccess(access); + buf.append(' ').append((name == null) ? "" : name) + .append('\n'); + text.add(buf.toString()); + } + @Override public Textifier visitAnnotationDefault() { text.add(tab2 + "default="); @@ -644,6 +686,12 @@ public Textifier visitMethodAnnotation(final String desc, return visitAnnotation(desc, visible); } + @Override + public Printer visitMethodTypeAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public Textifier visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { @@ -655,7 +703,7 @@ public Textifier visitParameterAnnotation(final int parameter, Textifier t = createTextifier(); text.add(t.getText()); text.add(visible ? ") // parameter " : ") // invisible, parameter "); - text.add(new Integer(parameter)); + text.add(parameter); text.add("\n"); return t; } @@ -761,9 +809,30 @@ public void visitFieldInsn(final int opcode, final String owner, text.add(buf.toString()); } + @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(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { buf.setLength(0); buf.append(tab2).append(OPCODES[opcode]).append(' '); appendDescriptor(INTERNAL_NAME, owner); @@ -781,26 +850,35 @@ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, buf.append(name); appendDescriptor(METHOD_DESCRIPTOR, desc); buf.append(" ["); + buf.append('\n'); + buf.append(tab3); appendHandle(bsm); + buf.append('\n'); buf.append(tab3).append("// arguments:"); if (bsmArgs.length == 0) { buf.append(" none"); } else { - buf.append('\n').append(tab3); + buf.append('\n'); for (int i = 0; i < bsmArgs.length; i++) { + buf.append(tab3); Object cst = bsmArgs[i]; if (cst instanceof String) { Printer.appendString(buf, (String) cst); } else if (cst instanceof Type) { - buf.append(((Type) cst).getDescriptor()).append(".class"); + Type type = (Type) cst; + if(type.getSort() == Type.METHOD){ + appendDescriptor(METHOD_DESCRIPTOR, type.getDescriptor()); + } else { + buf.append(type.getDescriptor()).append(".class"); + } } else if (cst instanceof Handle) { appendHandle((Handle) cst); } else { buf.append(cst); } - buf.append(", "); + buf.append(", \n"); } - buf.setLength(buf.length() - 2); + buf.setLength(buf.length() - 3); } buf.append('\n'); buf.append(tab2).append("]\n"); @@ -889,6 +967,12 @@ public void visitMultiANewArrayInsn(final String desc, final int dims) { text.add(buf.toString()); } + @Override + public Printer visitInsnAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -905,6 +989,25 @@ public void visitTryCatchBlock(final Label start, final Label end, text.add(buf.toString()); } + @Override + public Printer visitTryCatchAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + buf.setLength(0); + buf.append(tab2).append("TRYCATCHBLOCK @"); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + Textifier t = createTextifier(); + text.add(t.getText()); + buf.setLength(0); + buf.append(") : "); + appendTypeReference(typeRef); + buf.append(", ").append(typePath); + buf.append(visible ? "\n" : " // invisible\n"); + text.add(buf.toString()); + return t; + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -931,6 +1034,33 @@ public void visitLocalVariable(final String name, final String desc, text.add(buf.toString()); } + @Override + public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath, + Label[] start, Label[] end, int[] index, String desc, + boolean visible) { + buf.setLength(0); + buf.append(tab2).append("LOCALVARIABLE @"); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + Textifier t = createTextifier(); + text.add(t.getText()); + buf.setLength(0); + buf.append(") : "); + appendTypeReference(typeRef); + buf.append(", ").append(typePath); + for (int i = 0; i < start.length; ++i) { + buf.append(" [ "); + appendLabel(start[i]); + buf.append(" - "); + appendLabel(end[i]); + buf.append(" - ").append(index[i]).append(" ]"); + } + buf.append(visible ? "\n" : " // invisible\n"); + text.add(buf.toString()); + return t; + } + @Override public void visitLineNumber(final int line, final Label start) { buf.setLength(0); @@ -961,7 +1091,7 @@ public void visitMethodEnd() { /** * Prints a disassembled view of the given annotation. - * + * * @param desc * the class descriptor of the annotation class. * @param visible @@ -980,9 +1110,42 @@ public Textifier visitAnnotation(final String desc, final boolean visible) { return t; } + /** + * Prints a disassembled view of the given type annotation. + * + * @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 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. + */ + public Textifier visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + buf.setLength(0); + buf.append(tab).append('@'); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + Textifier t = createTextifier(); + text.add(t.getText()); + buf.setLength(0); + buf.append(") : "); + appendTypeReference(typeRef); + buf.append(", ").append(typePath); + buf.append(visible ? "\n" : " // invisible\n"); + text.add(buf.toString()); + return t; + } + /** * Prints a disassembled view of the given attribute. - * + * * @param attr * an attribute. */ @@ -1006,7 +1169,7 @@ public void visitAttribute(final Attribute attr) { /** * Creates a new TraceVisitor instance. - * + * * @return a new TraceVisitor. */ protected Textifier createTextifier() { @@ -1016,7 +1179,7 @@ protected Textifier createTextifier() { /** * Appends an internal name, a type descriptor or a type signature to * {@link #buf buf}. - * + * * @param type * indicates if desc is an internal name, a field descriptor, a * method descriptor, a class signature, ... @@ -1038,7 +1201,7 @@ protected void appendDescriptor(final int type, final String desc) { /** * Appends the name of the given label to {@link #buf buf}. Creates a new * label name if the given label does not yet have one. - * + * * @param l * a label. */ @@ -1056,15 +1219,15 @@ protected void appendLabel(final Label l) { /** * Appends the information about the given handle to {@link #buf buf}. - * + * * @param h * a handle, non null. */ protected void appendHandle(final Handle h) { - buf.append('\n').append(tab3); int tag = h.getTag(); buf.append("// handle kind 0x").append(Integer.toHexString(tag)) .append(" : "); + boolean isMethodHandle = false; switch (tag) { case Opcodes.H_GETFIELD: buf.append("GETFIELD"); @@ -1080,18 +1243,23 @@ protected void appendHandle(final Handle h) { break; case Opcodes.H_INVOKEINTERFACE: buf.append("INVOKEINTERFACE"); + isMethodHandle = true; break; case Opcodes.H_INVOKESPECIAL: buf.append("INVOKESPECIAL"); + isMethodHandle = true; break; case Opcodes.H_INVOKESTATIC: buf.append("INVOKESTATIC"); + isMethodHandle = true; break; case Opcodes.H_INVOKEVIRTUAL: buf.append("INVOKEVIRTUAL"); + isMethodHandle = true; break; case Opcodes.H_NEWINVOKESPECIAL: buf.append("NEWINVOKESPECIAL"); + isMethodHandle = true; break; } buf.append('\n'); @@ -1099,15 +1267,19 @@ protected void appendHandle(final Handle h) { appendDescriptor(INTERNAL_NAME, h.getOwner()); buf.append('.'); buf.append(h.getName()); - buf.append('('); + if(!isMethodHandle){ + buf.append('('); + } appendDescriptor(HANDLE_DESCRIPTOR, h.getDesc()); - buf.append(')').append('\n'); + if(!isMethodHandle){ + buf.append(')'); + } } /** * Appends a string representation of the given access modifiers to * {@link #buf buf}. - * + * * @param access * some access modifiers. */ @@ -1145,6 +1317,9 @@ private void appendAccess(final int access) { if ((access & Opcodes.ACC_SYNTHETIC) != 0) { buf.append("synthetic "); } + if ((access & Opcodes.ACC_MANDATED) != 0) { + buf.append("mandated "); + } if ((access & Opcodes.ACC_ENUM) != 0) { buf.append("enum "); } @@ -1156,6 +1331,90 @@ private void appendComa(final int i) { } } + private void appendTypeReference(final int typeRef) { + TypeReference ref = new TypeReference(typeRef); + switch (ref.getSort()) { + case TypeReference.CLASS_TYPE_PARAMETER: + buf.append("CLASS_TYPE_PARAMETER ").append( + ref.getTypeParameterIndex()); + break; + case TypeReference.METHOD_TYPE_PARAMETER: + buf.append("METHOD_TYPE_PARAMETER ").append( + ref.getTypeParameterIndex()); + break; + case TypeReference.CLASS_EXTENDS: + buf.append("CLASS_EXTENDS ").append(ref.getSuperTypeIndex()); + break; + case TypeReference.CLASS_TYPE_PARAMETER_BOUND: + buf.append("CLASS_TYPE_PARAMETER_BOUND ") + .append(ref.getTypeParameterIndex()).append(", ") + .append(ref.getTypeParameterBoundIndex()); + break; + case TypeReference.METHOD_TYPE_PARAMETER_BOUND: + buf.append("METHOD_TYPE_PARAMETER_BOUND ") + .append(ref.getTypeParameterIndex()).append(", ") + .append(ref.getTypeParameterBoundIndex()); + break; + case TypeReference.FIELD: + buf.append("FIELD"); + break; + case TypeReference.METHOD_RETURN: + buf.append("METHOD_RETURN"); + break; + case TypeReference.METHOD_RECEIVER: + buf.append("METHOD_RECEIVER"); + break; + case TypeReference.METHOD_FORMAL_PARAMETER: + buf.append("METHOD_FORMAL_PARAMETER ").append( + ref.getFormalParameterIndex()); + break; + case TypeReference.THROWS: + buf.append("THROWS ").append(ref.getExceptionIndex()); + break; + case TypeReference.LOCAL_VARIABLE: + buf.append("LOCAL_VARIABLE"); + break; + case TypeReference.RESOURCE_VARIABLE: + buf.append("RESOURCE_VARIABLE"); + break; + case TypeReference.EXCEPTION_PARAMETER: + buf.append("EXCEPTION_PARAMETER ").append( + ref.getTryCatchBlockIndex()); + break; + case TypeReference.INSTANCEOF: + buf.append("INSTANCEOF"); + break; + case TypeReference.NEW: + buf.append("NEW"); + break; + case TypeReference.CONSTRUCTOR_REFERENCE: + buf.append("CONSTRUCTOR_REFERENCE"); + break; + case TypeReference.METHOD_REFERENCE: + buf.append("METHOD_REFERENCE"); + break; + case TypeReference.CAST: + buf.append("CAST ").append(ref.getTypeArgumentIndex()); + break; + case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + buf.append("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: + buf.append("METHOD_INVOCATION_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + buf.append("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: + buf.append("METHOD_REFERENCE_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + } + } + private void appendFrameTypes(final int n, final Object[] o) { for (int i = 0; i < n; ++i) { if (i > 0) { diff --git a/src/java/nginx/clojure/asm/util/TraceAnnotationVisitor.java b/src/java/nginx/clojure/asm/util/TraceAnnotationVisitor.java index 6c4bae55..d2635a67 100644 --- a/src/java/nginx/clojure/asm/util/TraceAnnotationVisitor.java +++ b/src/java/nginx/clojure/asm/util/TraceAnnotationVisitor.java @@ -47,7 +47,7 @@ public TraceAnnotationVisitor(final Printer p) { } public TraceAnnotationVisitor(final AnnotationVisitor av, final Printer p) { - super(Opcodes.ASM4, av); + super(Opcodes.ASM5, av); this.p = p; } diff --git a/src/java/nginx/clojure/asm/util/TraceClassVisitor.java b/src/java/nginx/clojure/asm/util/TraceClassVisitor.java index 069b8b77..d598bd30 100644 --- a/src/java/nginx/clojure/asm/util/TraceClassVisitor.java +++ b/src/java/nginx/clojure/asm/util/TraceClassVisitor.java @@ -37,6 +37,7 @@ import nginx.clojure.asm.FieldVisitor; import nginx.clojure.asm.MethodVisitor; import nginx.clojure.asm.Opcodes; +import nginx.clojure.asm.TypePath; /** * A {@link ClassVisitor} that prints the classes it visits with a @@ -130,7 +131,7 @@ public TraceClassVisitor(final ClassVisitor cv, final PrintWriter pw) { */ public TraceClassVisitor(final ClassVisitor cv, final Printer p, final PrintWriter pw) { - super(Opcodes.ASM4, cv); + super(Opcodes.ASM5, cv); this.pw = pw; this.p = p; } @@ -165,6 +166,16 @@ public AnnotationVisitor visitAnnotation(final String desc, return new TraceAnnotationVisitor(av, p); } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitClassTypeAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = cv == null ? null : cv.visitTypeAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitAttribute(final Attribute attr) { p.visitClassAttribute(attr); diff --git a/src/java/nginx/clojure/asm/util/TraceFieldVisitor.java b/src/java/nginx/clojure/asm/util/TraceFieldVisitor.java index ad3a680f..4f4a3e25 100644 --- a/src/java/nginx/clojure/asm/util/TraceFieldVisitor.java +++ b/src/java/nginx/clojure/asm/util/TraceFieldVisitor.java @@ -33,6 +33,7 @@ import nginx.clojure.asm.Attribute; import nginx.clojure.asm.FieldVisitor; import nginx.clojure.asm.Opcodes; +import nginx.clojure.asm.TypePath; /** * A {@link FieldVisitor} that prints the fields it visits with a @@ -49,7 +50,7 @@ public TraceFieldVisitor(final Printer p) { } public TraceFieldVisitor(final FieldVisitor fv, final Printer p) { - super(Opcodes.ASM4, fv); + super(Opcodes.ASM5, fv); this.p = p; } @@ -62,6 +63,16 @@ public AnnotationVisitor visitAnnotation(final String desc, return new TraceAnnotationVisitor(av, p); } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitFieldTypeAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = fv == null ? null : fv.visitTypeAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitAttribute(final Attribute attr) { p.visitFieldAttribute(attr); diff --git a/src/java/nginx/clojure/asm/util/TraceMethodVisitor.java b/src/java/nginx/clojure/asm/util/TraceMethodVisitor.java index 0bf4573a..e3e7229a 100644 --- a/src/java/nginx/clojure/asm/util/TraceMethodVisitor.java +++ b/src/java/nginx/clojure/asm/util/TraceMethodVisitor.java @@ -35,6 +35,7 @@ import nginx.clojure.asm.Label; import nginx.clojure.asm.MethodVisitor; import nginx.clojure.asm.Opcodes; +import nginx.clojure.asm.TypePath; /** * A {@link MethodVisitor} that prints the methods it visits with a @@ -51,10 +52,16 @@ public TraceMethodVisitor(final Printer p) { } public TraceMethodVisitor(final MethodVisitor mv, final Printer p) { - super(Opcodes.ASM4, mv); + super(Opcodes.ASM5, mv); this.p = p; } + @Override + public void visitParameter(String name, int access) { + p.visitParameter(name, access); + super.visitParameter(name, access); + } + @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { @@ -64,6 +71,16 @@ public AnnotationVisitor visitAnnotation(final String desc, return new TraceAnnotationVisitor(av, p); } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitMethodTypeAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = mv == null ? null : mv.visitTypeAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitAttribute(final Attribute attr) { p.visitMethodAttribute(attr); @@ -130,11 +147,31 @@ public void visitFieldInsn(final int opcode, final String owner, super.visitFieldInsn(opcode, owner, name, desc); } + @Deprecated @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } p.visitMethodInsn(opcode, owner, name, desc); - super.visitMethodInsn(opcode, owner, name, desc); + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc); + } + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + p.visitMethodInsn(opcode, owner, name, desc, itf); + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } } @Override @@ -188,6 +225,16 @@ public void visitMultiANewArrayInsn(final String desc, final int dims) { super.visitMultiANewArrayInsn(desc, dims); } + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p + .visitInsnAnnotation(typeRef, typePath, desc, visible); + AnnotationVisitor av = mv == null ? null : mv.visitInsnAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -195,6 +242,16 @@ public void visitTryCatchBlock(final Label start, final Label end, super.visitTryCatchBlock(start, end, handler, type); } + @Override + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitTryCatchAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = mv == null ? null : mv.visitTryCatchAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -203,6 +260,18 @@ public void visitLocalVariable(final String name, final String desc, super.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) { + Printer p = this.p.visitLocalVariableAnnotation(typeRef, typePath, + start, end, index, desc, visible); + AnnotationVisitor av = mv == null ? null : mv + .visitLocalVariableAnnotation(typeRef, typePath, start, end, + index, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitLineNumber(final int line, final Label start) { p.visitLineNumber(line, start); diff --git a/src/java/nginx/clojure/asm/util/TraceSignatureVisitor.java b/src/java/nginx/clojure/asm/util/TraceSignatureVisitor.java index 2456f658..74760435 100644 --- a/src/java/nginx/clojure/asm/util/TraceSignatureVisitor.java +++ b/src/java/nginx/clojure/asm/util/TraceSignatureVisitor.java @@ -41,7 +41,7 @@ */ public final class TraceSignatureVisitor extends SignatureVisitor { - private final StringBuffer declaration; + private final StringBuilder declaration; private boolean isInterface; @@ -53,9 +53,9 @@ public final class TraceSignatureVisitor extends SignatureVisitor { private boolean seenInterface; - private StringBuffer returnType; + private StringBuilder returnType; - private StringBuffer exceptions; + private StringBuilder exceptions; /** * Stack used to keep track of class types that have arguments. Each element @@ -75,13 +75,13 @@ public final class TraceSignatureVisitor extends SignatureVisitor { private String separator = ""; public TraceSignatureVisitor(final int access) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); isInterface = (access & Opcodes.ACC_INTERFACE) != 0; - this.declaration = new StringBuffer(); + this.declaration = new StringBuilder(); } - private TraceSignatureVisitor(final StringBuffer buf) { - super(Opcodes.ASM4); + private TraceSignatureVisitor(final StringBuilder buf) { + super(Opcodes.ASM5); this.declaration = buf; } @@ -146,14 +146,14 @@ public SignatureVisitor visitReturnType() { declaration.append('('); } declaration.append(')'); - returnType = new StringBuffer(); + returnType = new StringBuilder(); return new TraceSignatureVisitor(returnType); } @Override public SignatureVisitor visitExceptionType() { if (exceptions == null) { - exceptions = new StringBuffer(); + exceptions = new StringBuilder(); } else { exceptions.append(", "); } diff --git a/src/java/nginx/clojure/asm/util/package.html b/src/java/nginx/clojure/asm/util/package.html new file mode 100644 index 00000000..91d74204 --- /dev/null +++ b/src/java/nginx/clojure/asm/util/package.html @@ -0,0 +1,40 @@ + + + +Provides ASM visitors that can be useful for programming and +debugging purposes. These class visitors are normally not used by applications +at runtime. This is why they are bundled in an optional asm-util.jar +library that is separated from (but requires) the asm.jar library, +which contains the core ASM framework. + +@since ASM 1.3.2 + + diff --git a/src/java/nginx/clojure/asm/xml/ASMContentHandler.java b/src/java/nginx/clojure/asm/xml/ASMContentHandler.java new file mode 100644 index 00000000..7cc77b8d --- /dev/null +++ b/src/java/nginx/clojure/asm/xml/ASMContentHandler.java @@ -0,0 +1,1464 @@ +/*** + * ASM XML Adapter + * Copyright (c) 2004-2011, Eugene Kuleshov + * 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.xml; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import nginx.clojure.asm.AnnotationVisitor; +import nginx.clojure.asm.ClassVisitor; +import nginx.clojure.asm.FieldVisitor; +import nginx.clojure.asm.Handle; +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 org.xml.sax.ContentHandler ContentHandler} that transforms XML + * document into Java class file. This class can be feeded by any kind of SAX + * 2.0 event producers, e.g. XML parser, XSLT or XPath engines, or custom code. + * + * @see nginx.clojure.asm.xml.SAXClassAdapter + * @see nginx.clojure.asm.xml.Processor + * + * @author Eugene Kuleshov + */ +public class ASMContentHandler extends DefaultHandler implements Opcodes { + + /** + * Stack of the intermediate processing contexts. + */ + private final ArrayList stack = new ArrayList(); + + /** + * Complete name of the current element. + */ + String match = ""; + + /** + * Current instance of the {@link ClassVisitor ClassVisitor} used to visit + * classfile bytecode. + */ + protected ClassVisitor cv; + + /** + * Map of the active {@link Label Label} instances for current method. + */ + protected Map labels; + + private static final String BASE = "class"; + + private final RuleSet RULES = new RuleSet(); + { + RULES.add(BASE, new ClassRule()); + RULES.add(BASE + "/interfaces/interface", new InterfaceRule()); + RULES.add(BASE + "/interfaces", new InterfacesRule()); + RULES.add(BASE + "/outerclass", new OuterClassRule()); + RULES.add(BASE + "/innerclass", new InnerClassRule()); + RULES.add(BASE + "/source", new SourceRule()); + RULES.add(BASE + "/field", new FieldRule()); + + RULES.add(BASE + "/method", new MethodRule()); + RULES.add(BASE + "/method/exceptions/exception", new ExceptionRule()); + RULES.add(BASE + "/method/exceptions", new ExceptionsRule()); + + RULES.add(BASE + "/method/parameter", new MethodParameterRule()); + RULES.add(BASE + "/method/annotationDefault", + new AnnotationDefaultRule()); + + RULES.add(BASE + "/method/code/*", new OpcodesRule()); // opcodes + + RULES.add(BASE + "/method/code/frame", new FrameRule()); + RULES.add(BASE + "/method/code/frame/local", new FrameTypeRule()); + RULES.add(BASE + "/method/code/frame/stack", new FrameTypeRule()); + + RULES.add(BASE + "/method/code/TABLESWITCH", new TableSwitchRule()); + RULES.add(BASE + "/method/code/TABLESWITCH/label", + new TableSwitchLabelRule()); + RULES.add(BASE + "/method/code/LOOKUPSWITCH", new LookupSwitchRule()); + RULES.add(BASE + "/method/code/LOOKUPSWITCH/label", + new LookupSwitchLabelRule()); + + RULES.add(BASE + "/method/code/INVOKEDYNAMIC", new InvokeDynamicRule()); + RULES.add(BASE + "/method/code/INVOKEDYNAMIC/bsmArg", + new InvokeDynamicBsmArgumentsRule()); + + RULES.add(BASE + "/method/code/Label", new LabelRule()); + RULES.add(BASE + "/method/code/TryCatch", new TryCatchRule()); + RULES.add(BASE + "/method/code/LineNumber", new LineNumberRule()); + RULES.add(BASE + "/method/code/LocalVar", new LocalVarRule()); + RULES.add(BASE + "/method/code/Max", new MaxRule()); + + RULES.add("*/annotation", new AnnotationRule()); + RULES.add("*/typeAnnotation", new TypeAnnotationRule()); + RULES.add("*/parameterAnnotation", new AnnotationParameterRule()); + RULES.add("*/insnAnnotation", new InsnAnnotationRule()); + RULES.add("*/tryCatchAnnotation", new TryCatchAnnotationRule()); + RULES.add("*/localVariableAnnotation", + new LocalVariableAnnotationRule()); + RULES.add("*/annotationValue", new AnnotationValueRule()); + RULES.add("*/annotationValueAnnotation", + new AnnotationValueAnnotationRule()); + RULES.add("*/annotationValueEnum", new AnnotationValueEnumRule()); + RULES.add("*/annotationValueArray", new AnnotationValueArrayRule()); + } + + private static interface OpcodeGroup { + public static final int INSN = 0; + public static final int INSN_INT = 1; + public static final int INSN_VAR = 2; + public static final int INSN_TYPE = 3; + public static final int INSN_FIELD = 4; + public static final int INSN_METHOD = 5; + public static final int INSN_JUMP = 6; + public static final int INSN_LDC = 7; + public static final int INSN_IINC = 8; + public static final int INSN_MULTIANEWARRAY = 9; + } + + /** + * Map of the opcode names to opcode and opcode group + */ + static final HashMap OPCODES = new HashMap(); + static { + addOpcode("NOP", NOP, OpcodeGroup.INSN); + addOpcode("ACONST_NULL", ACONST_NULL, OpcodeGroup.INSN); + addOpcode("ICONST_M1", ICONST_M1, OpcodeGroup.INSN); + addOpcode("ICONST_0", ICONST_0, OpcodeGroup.INSN); + addOpcode("ICONST_1", ICONST_1, OpcodeGroup.INSN); + addOpcode("ICONST_2", ICONST_2, OpcodeGroup.INSN); + addOpcode("ICONST_3", ICONST_3, OpcodeGroup.INSN); + addOpcode("ICONST_4", ICONST_4, OpcodeGroup.INSN); + addOpcode("ICONST_5", ICONST_5, OpcodeGroup.INSN); + addOpcode("LCONST_0", LCONST_0, OpcodeGroup.INSN); + addOpcode("LCONST_1", LCONST_1, OpcodeGroup.INSN); + addOpcode("FCONST_0", FCONST_0, OpcodeGroup.INSN); + addOpcode("FCONST_1", FCONST_1, OpcodeGroup.INSN); + addOpcode("FCONST_2", FCONST_2, OpcodeGroup.INSN); + addOpcode("DCONST_0", DCONST_0, OpcodeGroup.INSN); + addOpcode("DCONST_1", DCONST_1, OpcodeGroup.INSN); + addOpcode("BIPUSH", BIPUSH, OpcodeGroup.INSN_INT); + addOpcode("SIPUSH", SIPUSH, OpcodeGroup.INSN_INT); + addOpcode("LDC", LDC, OpcodeGroup.INSN_LDC); + addOpcode("ILOAD", ILOAD, OpcodeGroup.INSN_VAR); + addOpcode("LLOAD", LLOAD, OpcodeGroup.INSN_VAR); + addOpcode("FLOAD", FLOAD, OpcodeGroup.INSN_VAR); + addOpcode("DLOAD", DLOAD, OpcodeGroup.INSN_VAR); + addOpcode("ALOAD", ALOAD, OpcodeGroup.INSN_VAR); + addOpcode("IALOAD", IALOAD, OpcodeGroup.INSN); + addOpcode("LALOAD", LALOAD, OpcodeGroup.INSN); + addOpcode("FALOAD", FALOAD, OpcodeGroup.INSN); + addOpcode("DALOAD", DALOAD, OpcodeGroup.INSN); + addOpcode("AALOAD", AALOAD, OpcodeGroup.INSN); + addOpcode("BALOAD", BALOAD, OpcodeGroup.INSN); + addOpcode("CALOAD", CALOAD, OpcodeGroup.INSN); + addOpcode("SALOAD", SALOAD, OpcodeGroup.INSN); + addOpcode("ISTORE", ISTORE, OpcodeGroup.INSN_VAR); + addOpcode("LSTORE", LSTORE, OpcodeGroup.INSN_VAR); + addOpcode("FSTORE", FSTORE, OpcodeGroup.INSN_VAR); + addOpcode("DSTORE", DSTORE, OpcodeGroup.INSN_VAR); + addOpcode("ASTORE", ASTORE, OpcodeGroup.INSN_VAR); + addOpcode("IASTORE", IASTORE, OpcodeGroup.INSN); + addOpcode("LASTORE", LASTORE, OpcodeGroup.INSN); + addOpcode("FASTORE", FASTORE, OpcodeGroup.INSN); + addOpcode("DASTORE", DASTORE, OpcodeGroup.INSN); + addOpcode("AASTORE", AASTORE, OpcodeGroup.INSN); + addOpcode("BASTORE", BASTORE, OpcodeGroup.INSN); + addOpcode("CASTORE", CASTORE, OpcodeGroup.INSN); + addOpcode("SASTORE", SASTORE, OpcodeGroup.INSN); + addOpcode("POP", POP, OpcodeGroup.INSN); + addOpcode("POP2", POP2, OpcodeGroup.INSN); + addOpcode("DUP", DUP, OpcodeGroup.INSN); + addOpcode("DUP_X1", DUP_X1, OpcodeGroup.INSN); + addOpcode("DUP_X2", DUP_X2, OpcodeGroup.INSN); + addOpcode("DUP2", DUP2, OpcodeGroup.INSN); + addOpcode("DUP2_X1", DUP2_X1, OpcodeGroup.INSN); + addOpcode("DUP2_X2", DUP2_X2, OpcodeGroup.INSN); + addOpcode("SWAP", SWAP, OpcodeGroup.INSN); + addOpcode("IADD", IADD, OpcodeGroup.INSN); + addOpcode("LADD", LADD, OpcodeGroup.INSN); + addOpcode("FADD", FADD, OpcodeGroup.INSN); + addOpcode("DADD", DADD, OpcodeGroup.INSN); + addOpcode("ISUB", ISUB, OpcodeGroup.INSN); + addOpcode("LSUB", LSUB, OpcodeGroup.INSN); + addOpcode("FSUB", FSUB, OpcodeGroup.INSN); + addOpcode("DSUB", DSUB, OpcodeGroup.INSN); + addOpcode("IMUL", IMUL, OpcodeGroup.INSN); + addOpcode("LMUL", LMUL, OpcodeGroup.INSN); + addOpcode("FMUL", FMUL, OpcodeGroup.INSN); + addOpcode("DMUL", DMUL, OpcodeGroup.INSN); + addOpcode("IDIV", IDIV, OpcodeGroup.INSN); + addOpcode("LDIV", LDIV, OpcodeGroup.INSN); + addOpcode("FDIV", FDIV, OpcodeGroup.INSN); + addOpcode("DDIV", DDIV, OpcodeGroup.INSN); + addOpcode("IREM", IREM, OpcodeGroup.INSN); + addOpcode("LREM", LREM, OpcodeGroup.INSN); + addOpcode("FREM", FREM, OpcodeGroup.INSN); + addOpcode("DREM", DREM, OpcodeGroup.INSN); + addOpcode("INEG", INEG, OpcodeGroup.INSN); + addOpcode("LNEG", LNEG, OpcodeGroup.INSN); + addOpcode("FNEG", FNEG, OpcodeGroup.INSN); + addOpcode("DNEG", DNEG, OpcodeGroup.INSN); + addOpcode("ISHL", ISHL, OpcodeGroup.INSN); + addOpcode("LSHL", LSHL, OpcodeGroup.INSN); + addOpcode("ISHR", ISHR, OpcodeGroup.INSN); + addOpcode("LSHR", LSHR, OpcodeGroup.INSN); + addOpcode("IUSHR", IUSHR, OpcodeGroup.INSN); + addOpcode("LUSHR", LUSHR, OpcodeGroup.INSN); + addOpcode("IAND", IAND, OpcodeGroup.INSN); + addOpcode("LAND", LAND, OpcodeGroup.INSN); + addOpcode("IOR", IOR, OpcodeGroup.INSN); + addOpcode("LOR", LOR, OpcodeGroup.INSN); + addOpcode("IXOR", IXOR, OpcodeGroup.INSN); + addOpcode("LXOR", LXOR, OpcodeGroup.INSN); + addOpcode("IINC", IINC, OpcodeGroup.INSN_IINC); + addOpcode("I2L", I2L, OpcodeGroup.INSN); + addOpcode("I2F", I2F, OpcodeGroup.INSN); + addOpcode("I2D", I2D, OpcodeGroup.INSN); + addOpcode("L2I", L2I, OpcodeGroup.INSN); + addOpcode("L2F", L2F, OpcodeGroup.INSN); + addOpcode("L2D", L2D, OpcodeGroup.INSN); + addOpcode("F2I", F2I, OpcodeGroup.INSN); + addOpcode("F2L", F2L, OpcodeGroup.INSN); + addOpcode("F2D", F2D, OpcodeGroup.INSN); + addOpcode("D2I", D2I, OpcodeGroup.INSN); + addOpcode("D2L", D2L, OpcodeGroup.INSN); + addOpcode("D2F", D2F, OpcodeGroup.INSN); + addOpcode("I2B", I2B, OpcodeGroup.INSN); + addOpcode("I2C", I2C, OpcodeGroup.INSN); + addOpcode("I2S", I2S, OpcodeGroup.INSN); + addOpcode("LCMP", LCMP, OpcodeGroup.INSN); + addOpcode("FCMPL", FCMPL, OpcodeGroup.INSN); + addOpcode("FCMPG", FCMPG, OpcodeGroup.INSN); + addOpcode("DCMPL", DCMPL, OpcodeGroup.INSN); + addOpcode("DCMPG", DCMPG, OpcodeGroup.INSN); + addOpcode("IFEQ", IFEQ, OpcodeGroup.INSN_JUMP); + addOpcode("IFNE", IFNE, OpcodeGroup.INSN_JUMP); + addOpcode("IFLT", IFLT, OpcodeGroup.INSN_JUMP); + addOpcode("IFGE", IFGE, OpcodeGroup.INSN_JUMP); + addOpcode("IFGT", IFGT, OpcodeGroup.INSN_JUMP); + addOpcode("IFLE", IFLE, OpcodeGroup.INSN_JUMP); + addOpcode("IF_ICMPEQ", IF_ICMPEQ, OpcodeGroup.INSN_JUMP); + addOpcode("IF_ICMPNE", IF_ICMPNE, OpcodeGroup.INSN_JUMP); + addOpcode("IF_ICMPLT", IF_ICMPLT, OpcodeGroup.INSN_JUMP); + addOpcode("IF_ICMPGE", IF_ICMPGE, OpcodeGroup.INSN_JUMP); + addOpcode("IF_ICMPGT", IF_ICMPGT, OpcodeGroup.INSN_JUMP); + addOpcode("IF_ICMPLE", IF_ICMPLE, OpcodeGroup.INSN_JUMP); + addOpcode("IF_ACMPEQ", IF_ACMPEQ, OpcodeGroup.INSN_JUMP); + addOpcode("IF_ACMPNE", IF_ACMPNE, OpcodeGroup.INSN_JUMP); + addOpcode("GOTO", GOTO, OpcodeGroup.INSN_JUMP); + addOpcode("JSR", JSR, OpcodeGroup.INSN_JUMP); + addOpcode("RET", RET, OpcodeGroup.INSN_VAR); + addOpcode("IRETURN", IRETURN, OpcodeGroup.INSN); + addOpcode("LRETURN", LRETURN, OpcodeGroup.INSN); + addOpcode("FRETURN", FRETURN, OpcodeGroup.INSN); + addOpcode("DRETURN", DRETURN, OpcodeGroup.INSN); + addOpcode("ARETURN", ARETURN, OpcodeGroup.INSN); + addOpcode("RETURN", RETURN, OpcodeGroup.INSN); + addOpcode("GETSTATIC", GETSTATIC, OpcodeGroup.INSN_FIELD); + addOpcode("PUTSTATIC", PUTSTATIC, OpcodeGroup.INSN_FIELD); + addOpcode("GETFIELD", GETFIELD, OpcodeGroup.INSN_FIELD); + addOpcode("PUTFIELD", PUTFIELD, OpcodeGroup.INSN_FIELD); + addOpcode("INVOKEVIRTUAL", INVOKEVIRTUAL, OpcodeGroup.INSN_METHOD); + addOpcode("INVOKESPECIAL", INVOKESPECIAL, OpcodeGroup.INSN_METHOD); + addOpcode("INVOKESTATIC", INVOKESTATIC, OpcodeGroup.INSN_METHOD); + addOpcode("INVOKEINTERFACE", INVOKEINTERFACE, OpcodeGroup.INSN_METHOD); + addOpcode("NEW", NEW, OpcodeGroup.INSN_TYPE); + addOpcode("NEWARRAY", NEWARRAY, OpcodeGroup.INSN_INT); + addOpcode("ANEWARRAY", ANEWARRAY, OpcodeGroup.INSN_TYPE); + addOpcode("ARRAYLENGTH", ARRAYLENGTH, OpcodeGroup.INSN); + addOpcode("ATHROW", ATHROW, OpcodeGroup.INSN); + addOpcode("CHECKCAST", CHECKCAST, OpcodeGroup.INSN_TYPE); + addOpcode("INSTANCEOF", INSTANCEOF, OpcodeGroup.INSN_TYPE); + addOpcode("MONITORENTER", MONITORENTER, OpcodeGroup.INSN); + addOpcode("MONITOREXIT", MONITOREXIT, OpcodeGroup.INSN); + addOpcode("MULTIANEWARRAY", MULTIANEWARRAY, + OpcodeGroup.INSN_MULTIANEWARRAY); + addOpcode("IFNULL", IFNULL, OpcodeGroup.INSN_JUMP); + addOpcode("IFNONNULL", IFNONNULL, OpcodeGroup.INSN_JUMP); + } + + private static void addOpcode(String operStr, int oper, int group) { + OPCODES.put(operStr, new Opcode(oper, group)); + } + + static final HashMap TYPES = new HashMap(); + static { + String[] types = SAXCodeAdapter.TYPES; + for (int i = 0; i < types.length; i++) { + TYPES.put(types[i], i); + } + } + + /** + * Constructs a new {@link ASMContentHandler ASMContentHandler} object. + * + * @param cv + * class visitor that will be called to reconstruct the classfile + * using the XML stream. + */ + public ASMContentHandler(final ClassVisitor cv) { + this.cv = cv; + } + + /** + * Process notification of the start of an XML element being reached. + * + * @param ns + * - The Namespace URI, or the empty string if the element has no + * Namespace URI or if Namespace processing is not being + * performed. + * @param lName + * - The local name (without prefix), or the empty string if + * Namespace processing is not being performed. + * @param qName + * - The qualified name (with prefix), or the empty string if + * qualified names are not available. + * @param list + * - The attributes attached to the element. If there are no + * attributes, it shall be an empty Attributes object. + * @exception SAXException + * if a parsing error is to be reported + */ + @Override + public final void startElement(final String ns, final String lName, + final String qName, final Attributes list) throws SAXException { + // the actual element name is either in lName or qName, depending + // on whether the parser is namespace aware + String name = lName == null || lName.length() == 0 ? qName : lName; + + // Compute the current matching rule + StringBuilder sb = new StringBuilder(match); + if (match.length() > 0) { + sb.append('/'); + } + sb.append(name); + match = sb.toString(); + + // Fire "begin" events for all relevant rules + Rule r = (Rule) RULES.match(match); + if (r != null) { + r.begin(name, list); + } + } + + /** + * Process notification of the end of an XML element being reached. + * + * @param ns + * - The Namespace URI, or the empty string if the element has no + * Namespace URI or if Namespace processing is not being + * performed. + * @param lName + * - The local name (without prefix), or the empty string if + * Namespace processing is not being performed. + * @param qName + * - The qualified XML 1.0 name (with prefix), or the empty + * string if qualified names are not available. + * + * @exception SAXException + * if a parsing error is to be reported + */ + @Override + public final void endElement(final String ns, final String lName, + final String qName) throws SAXException { + // the actual element name is either in lName or qName, depending + // on whether the parser is namespace aware + String name = lName == null || lName.length() == 0 ? qName : lName; + + // Fire "end" events for all relevant rules in reverse order + Rule r = (Rule) RULES.match(match); + if (r != null) { + r.end(name); + } + + // Recover the previous match expression + int slash = match.lastIndexOf('/'); + if (slash >= 0) { + match = match.substring(0, slash); + } else { + match = ""; + } + } + + /** + * Return the top object on the stack without removing it. If there are no + * objects on the stack, return null. + * + * @return the top object on the stack without removing it. + */ + final Object peek() { + int size = stack.size(); + return size == 0 ? null : stack.get(size - 1); + } + + /** + * Pop the top object off of the stack, and return it. If there are no + * objects on the stack, return null. + * + * @return the top object off of the stack. + */ + final Object pop() { + int size = stack.size(); + return size == 0 ? null : stack.remove(size - 1); + } + + /** + * Push a new object onto the top of the object stack. + * + * @param object + * The new object + */ + final void push(final Object object) { + stack.add(object); + } + + static final class RuleSet { + + private final HashMap rules = new HashMap(); + + private final ArrayList lpatterns = new ArrayList(); + + private final ArrayList rpatterns = new ArrayList(); + + public void add(final String path, final Object rule) { + String pattern = path; + if (path.startsWith("*/")) { + pattern = path.substring(1); + lpatterns.add(pattern); + } else if (path.endsWith("/*")) { + pattern = path.substring(0, path.length() - 1); + rpatterns.add(pattern); + } + rules.put(pattern, rule); + } + + public Object match(final String path) { + if (rules.containsKey(path)) { + return rules.get(path); + } + + int n = path.lastIndexOf('/'); + for (Iterator it = lpatterns.iterator(); it.hasNext();) { + String pattern = it.next(); + if (path.substring(n).endsWith(pattern)) { + return rules.get(pattern); + } + } + + for (Iterator it = rpatterns.iterator(); it.hasNext();) { + String pattern = it.next(); + if (path.startsWith(pattern)) { + return rules.get(pattern); + } + } + + return null; + } + } + + /** + * Rule + */ + protected abstract class Rule { + + public void begin(final String name, final Attributes attrs) + throws SAXException { + } + + public void end(final String name) { + } + + protected final Object getValue(final String desc, final String val) + throws SAXException { + Object value = null; + if (val != null) { + if ("Ljava/lang/String;".equals(desc)) { + value = decode(val); + } else if ("Ljava/lang/Integer;".equals(desc) + || "I".equals(desc) || "S".equals(desc) + || "B".equals(desc) || "C".equals(desc) + || "Z".equals(desc)) { + value = new Integer(val); + + } else if ("Ljava/lang/Short;".equals(desc)) { + value = new Short(val); + + } else if ("Ljava/lang/Byte;".equals(desc)) { + value = new Byte(val); + + } else if ("Ljava/lang/Character;".equals(desc)) { + value = new Character(decode(val).charAt(0)); + + } else if ("Ljava/lang/Boolean;".equals(desc)) { + value = Boolean.valueOf(val); + + } else if ("Ljava/lang/Long;".equals(desc) || "J".equals(desc)) { + value = new Long(val); + } else if ("Ljava/lang/Float;".equals(desc) || "F".equals(desc)) { + value = new Float(val); + } else if ("Ljava/lang/Double;".equals(desc) + || "D".equals(desc)) { + value = new Double(val); + } else if (Type.getDescriptor(Type.class).equals(desc)) { + value = Type.getType(val); + + } else if (Type.getDescriptor(Handle.class).equals(desc)) { + value = decodeHandle(val); + + } else { + // TODO use of default toString(). + throw new SAXException("Invalid value:" + val + " desc:" + + desc + " ctx:" + this); + } + } + return value; + } + + Handle decodeHandle(final String val) throws SAXException { + try { + int dotIndex = val.indexOf('.'); + int descIndex = val.indexOf('(', dotIndex + 1); + int tagIndex = val.lastIndexOf('('); + int itfIndex = val.indexOf(' ', tagIndex + 1); + + boolean itf = itfIndex != -1; + int tag = Integer.parseInt( + val.substring(tagIndex + 1, + itf? val.length() - 1: itfIndex)); + String owner = val.substring(0, dotIndex); + String name = val.substring(dotIndex + 1, descIndex); + String desc = val.substring(descIndex, tagIndex - 1); + return new Handle(tag, owner, name, desc, itf); + + } catch (RuntimeException e) { + throw new SAXException("Malformed handle " + val, e); + } + } + + private final String decode(final String val) throws SAXException { + StringBuilder sb = new StringBuilder(val.length()); + try { + int n = 0; + while (n < val.length()) { + char c = val.charAt(n); + if (c == '\\') { + n++; + c = val.charAt(n); + if (c == '\\') { + sb.append('\\'); + } else { + n++; // skip 'u' + sb.append((char) Integer.parseInt( + val.substring(n, n + 4), 16)); + n += 3; + } + } else { + sb.append(c); + } + n++; + } + + } catch (RuntimeException ex) { + throw new SAXException(ex); + } + return sb.toString(); + } + + protected final Label getLabel(final Object label) { + Label lbl = labels.get(label); + if (lbl == null) { + lbl = new Label(); + labels.put(label, lbl); + } + return lbl; + } + + // TODO verify move to stack + protected final MethodVisitor getCodeVisitor() { + return (MethodVisitor) peek(); + } + + protected final int getAccess(final String s) { + int access = 0; + if (s.indexOf("public") != -1) { + access |= ACC_PUBLIC; + } + if (s.indexOf("private") != -1) { + access |= ACC_PRIVATE; + } + if (s.indexOf("protected") != -1) { + access |= ACC_PROTECTED; + } + if (s.indexOf("static") != -1) { + access |= ACC_STATIC; + } + if (s.indexOf("final") != -1) { + access |= ACC_FINAL; + } + if (s.indexOf("super") != -1) { + access |= ACC_SUPER; + } + if (s.indexOf("synchronized") != -1) { + access |= ACC_SYNCHRONIZED; + } + if (s.indexOf("volatile") != -1) { + access |= ACC_VOLATILE; + } + if (s.indexOf("bridge") != -1) { + access |= ACC_BRIDGE; + } + if (s.indexOf("varargs") != -1) { + access |= ACC_VARARGS; + } + if (s.indexOf("transient") != -1) { + access |= ACC_TRANSIENT; + } + if (s.indexOf("native") != -1) { + access |= ACC_NATIVE; + } + if (s.indexOf("interface") != -1) { + access |= ACC_INTERFACE; + } + if (s.indexOf("abstract") != -1) { + access |= ACC_ABSTRACT; + } + if (s.indexOf("strict") != -1) { + access |= ACC_STRICT; + } + if (s.indexOf("synthetic") != -1) { + access |= ACC_SYNTHETIC; + } + if (s.indexOf("annotation") != -1) { + access |= ACC_ANNOTATION; + } + if (s.indexOf("enum") != -1) { + access |= ACC_ENUM; + } + if (s.indexOf("deprecated") != -1) { + access |= ACC_DEPRECATED; + } + if (s.indexOf("mandated") != -1) { + access |= ACC_MANDATED; + } + return access; + } + } + + /** + * ClassRule + */ + final class ClassRule extends Rule { + + @Override + public final void begin(final String name, final Attributes attrs) { + int major = Integer.parseInt(attrs.getValue("major")); + int minor = Integer.parseInt(attrs.getValue("minor")); + HashMap vals = new HashMap(); + vals.put("version", minor << 16 | major); + vals.put("access", attrs.getValue("access")); + vals.put("name", attrs.getValue("name")); + vals.put("parent", attrs.getValue("parent")); + vals.put("source", attrs.getValue("source")); + vals.put("signature", attrs.getValue("signature")); + vals.put("interfaces", new ArrayList()); + push(vals); + // values will be extracted in InterfacesRule.end(); + } + } + + final class SourceRule extends Rule { + + @Override + public void begin(final String name, final Attributes attrs) { + String file = attrs.getValue("file"); + String debug = attrs.getValue("debug"); + cv.visitSource(file, debug); + } + } + + /** + * InterfaceRule + */ + final class InterfaceRule extends Rule { + + @Override + @SuppressWarnings("unchecked") + public final void begin(final String name, final Attributes attrs) { + ((ArrayList) ((HashMap) peek()).get("interfaces")) + .add(attrs.getValue("name")); + } + } + + /** + * InterfacesRule + */ + final class InterfacesRule extends Rule { + + @Override + public final void end(final String element) { + HashMap vals = (HashMap) pop(); + int version = ((Integer) vals.get("version")).intValue(); + int access = getAccess((String) vals.get("access")); + String name = (String) vals.get("name"); + String signature = (String) vals.get("signature"); + String parent = (String) vals.get("parent"); + ArrayList infs = (ArrayList) vals.get("interfaces"); + String[] interfaces = infs.toArray(new String[infs.size()]); + cv.visit(version, access, name, signature, parent, interfaces); + push(cv); + } + } + + /** + * OuterClassRule + */ + final class OuterClassRule extends Rule { + + @Override + public final void begin(final String element, final Attributes attrs) { + String owner = attrs.getValue("owner"); + String name = attrs.getValue("name"); + String desc = attrs.getValue("desc"); + cv.visitOuterClass(owner, name, desc); + } + } + + /** + * InnerClassRule + */ + final class InnerClassRule extends Rule { + + @Override + public final void begin(final String element, final Attributes attrs) { + int access = getAccess(attrs.getValue("access")); + String name = attrs.getValue("name"); + String outerName = attrs.getValue("outerName"); + String innerName = attrs.getValue("innerName"); + cv.visitInnerClass(name, outerName, innerName, access); + } + } + + /** + * FieldRule + */ + final class FieldRule extends Rule { + + @Override + public final void begin(final String element, final Attributes attrs) + throws SAXException { + int access = getAccess(attrs.getValue("access")); + String name = attrs.getValue("name"); + String signature = attrs.getValue("signature"); + String desc = attrs.getValue("desc"); + Object value = getValue(desc, attrs.getValue("value")); + push(cv.visitField(access, name, desc, signature, value)); + } + + @Override + public void end(final String name) { + ((FieldVisitor) pop()).visitEnd(); + } + } + + /** + * MethodRule + */ + final class MethodRule extends Rule { + + @Override + public final void begin(final String name, final Attributes attrs) { + labels = new HashMap(); + HashMap vals = new HashMap(); + vals.put("access", attrs.getValue("access")); + vals.put("name", attrs.getValue("name")); + vals.put("desc", attrs.getValue("desc")); + vals.put("signature", attrs.getValue("signature")); + vals.put("exceptions", new ArrayList()); + push(vals); + // values will be extracted in ExceptionsRule.end(); + } + + @Override + public final void end(final String name) { + ((MethodVisitor) pop()).visitEnd(); + labels = null; + } + } + + /** + * ExceptionRule + */ + final class ExceptionRule extends Rule { + + @Override + @SuppressWarnings("unchecked") + public final void begin(final String name, final Attributes attrs) { + ((ArrayList) ((HashMap) peek()).get("exceptions")) + .add(attrs.getValue("name")); + } + } + + /** + * ExceptionsRule + */ + final class ExceptionsRule extends Rule { + + @Override + public final void end(final String element) { + HashMap vals = (HashMap) pop(); + int access = getAccess((String) vals.get("access")); + String name = (String) vals.get("name"); + String desc = (String) vals.get("desc"); + String signature = (String) vals.get("signature"); + ArrayList excs = (ArrayList) vals.get("exceptions"); + String[] exceptions = excs.toArray(new String[excs.size()]); + + push(cv.visitMethod(access, name, desc, signature, exceptions)); + } + } + + /** + * MethodParameterRule + */ + final class MethodParameterRule extends Rule { + @Override + public void begin(final String nm, final Attributes attrs) { + String name = attrs.getValue("name"); + int access = getAccess(attrs.getValue("access")); + getCodeVisitor().visitParameter(name, access); + } + } + + /** + * TableSwitchRule + */ + final class TableSwitchRule extends Rule { + + @Override + public final void begin(final String name, final Attributes attrs) { + HashMap vals = new HashMap(); + vals.put("min", attrs.getValue("min")); + vals.put("max", attrs.getValue("max")); + vals.put("dflt", attrs.getValue("dflt")); + vals.put("labels", new ArrayList()); + push(vals); + } + + @Override + public final void end(final String name) { + HashMap vals = (HashMap) pop(); + int min = Integer.parseInt((String) vals.get("min")); + int max = Integer.parseInt((String) vals.get("max")); + Label dflt = getLabel(vals.get("dflt")); + ArrayList lbls = (ArrayList) vals.get("labels"); + Label[] labels = lbls.toArray(new Label[lbls.size()]); + getCodeVisitor().visitTableSwitchInsn(min, max, dflt, labels); + } + } + + /** + * TableSwitchLabelRule + */ + final class TableSwitchLabelRule extends Rule { + + @Override + @SuppressWarnings("unchecked") + public final void begin(final String name, final Attributes attrs) { + ((ArrayList

    +The ASM framework is organized +around the {@link nginx.clojure.asm.ClassVisitor ClassVisitor}, +{@link nginx.clojure.asm.FieldVisitor FieldVisitor}, +{@link nginx.clojure.asm.MethodVisitor MethodVisitor} and +{@link nginx.clojure.asm.AnnotationVisitor AnnotationVisitor} abstract classes, +which allow one to visit the fields, methods and annotations of a class, +including the bytecode instructions of each method. + +